UNPKG

wingman-monitor

Version:

Runtime error monitoring package with React provider that automatically reports errors to webhook endpoints

241 lines 10.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.WingmanProvider = void 0; exports.useWingman = useWingman; exports.useWingmanReporting = useWingmanReporting; exports.useWingmanConfig = useWingmanConfig; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = __importStar(require("react")); const monitor_1 = require("./monitor"); const errorFiltering_1 = require("./utils/errorFiltering"); const tokenResolver_1 = require("./utils/tokenResolver"); const WingmanContext = (0, react_1.createContext)({ monitor: null, isActive: false, }); class WingmanProvider extends react_1.default.Component { constructor(props) { super(props); this.isActive = false; this.state = { hasError: false, isInitialized: false }; this.monitor = new monitor_1.WingmanMonitor(props.projectPath); } static getDerivedStateFromError(error) { return { hasError: true, error, isInitialized: false }; } componentDidCatch(error, errorInfo) { // Check if we should report this error based on environment and filters if (this.isActive && (0, errorFiltering_1.shouldReportError)(error, { type: 'React Component Error' })) { this.monitor.reportCustomError(error, { componentStack: errorInfo.componentStack, errorBoundary: true, type: 'React Component Error', }); } } async componentDidMount() { const { autoStart = true } = this.props; const envConfig = (0, errorFiltering_1.getEnvironmentConfig)(); if (autoStart) { try { // Try to get the access token from .wingman.json const accessToken = await (0, tokenResolver_1.requireAccessToken)(); // Update monitor configuration with the token this.monitor = new monitor_1.WingmanMonitor(this.props.projectPath); await this.monitor.start(); this.isActive = this.monitor.isActive(); // Update state to indicate successful initialization this.setState({ isInitialized: true }); // Only set up global error listeners in environments where we want to report errors const shouldEnableErrorReporting = this.props.enableErrorReporting ?? envConfig.enableErrorReporting; if (shouldEnableErrorReporting && this.isActive) { this.setupGlobalErrorHandlers(); } } catch (error) { console.error('Wingman: Failed to start monitoring in provider:', error); this.setState({ initError: error instanceof Error ? error.message : 'Failed to initialize Wingman', isInitialized: false }); } } } setupGlobalErrorHandlers() { // Handle uncaught JavaScript errors this.handleError = (event) => { const error = event.error || new Error(event.message); const context = { type: 'global_error', filename: event.filename, lineno: event.lineno, colno: event.colno, }; if ((0, errorFiltering_1.shouldReportError)(error, context)) { this.monitor.reportCustomError(error, context); } }; // Handle unhandled promise rejections this.handleRejection = (event) => { const error = event.reason instanceof Error ? event.reason : new Error(String(event.reason)); const context = { type: 'unhandled_promise_rejection', reason: event.reason, }; if ((0, errorFiltering_1.shouldReportError)(error, context)) { this.monitor.reportCustomError(error, context); } }; window.addEventListener('error', this.handleError); window.addEventListener('unhandledrejection', this.handleRejection); } removeGlobalErrorHandlers() { if (this.handleError) { window.removeEventListener('error', this.handleError); } if (this.handleRejection) { window.removeEventListener('unhandledrejection', this.handleRejection); } } componentWillUnmount() { // Clean up global error handlers this.removeGlobalErrorHandlers(); if (this.isActive) { this.monitor.stop(); this.isActive = false; } } render() { const { children, enableErrorBoundary = true } = this.props; const { hasError, error, initError, isInitialized } = this.state; const contextValue = { monitor: this.monitor, isActive: this.isActive, }; // Show initialization error in development only if (initError && process.env.NODE_ENV === 'development') { return ((0, jsx_runtime_1.jsxs)(WingmanContext.Provider, { value: contextValue, children: [(0, jsx_runtime_1.jsxs)("div", { style: { padding: '20px', backgroundColor: '#fff3cd', border: '1px solid #ffeaa7', borderRadius: '4px', margin: '20px' }, children: [(0, jsx_runtime_1.jsx)("h3", { children: "Wingman Configuration Error" }), (0, jsx_runtime_1.jsx)("p", { children: initError }), (0, jsx_runtime_1.jsxs)("p", { style: { fontSize: '14px', color: '#666' }, children: ["Run: ", (0, jsx_runtime_1.jsx)("code", { children: "npx wingman init <your-access-token>" })] })] }), children] })); } if (enableErrorBoundary && hasError) { return ((0, jsx_runtime_1.jsx)(WingmanContext.Provider, { value: contextValue, children: (0, jsx_runtime_1.jsxs)("div", { style: { padding: '20px', backgroundColor: '#fee', border: '1px solid #fcc', borderRadius: '4px', margin: '20px' }, children: [(0, jsx_runtime_1.jsx)("h2", { children: "Something went wrong" }), (0, jsx_runtime_1.jsx)("p", { children: "An error occurred in the application. The error has been reported." }), process.env.NODE_ENV === 'development' && error && ((0, jsx_runtime_1.jsxs)("details", { style: { marginTop: '10px' }, children: [(0, jsx_runtime_1.jsx)("summary", { children: "Error details (development only)" }), (0, jsx_runtime_1.jsx)("pre", { style: { backgroundColor: '#f5f5f5', padding: '10px', overflow: 'auto', marginTop: '10px' }, children: error.stack })] }))] }) })); } return ((0, jsx_runtime_1.jsx)(WingmanContext.Provider, { value: contextValue, children: children })); } } exports.WingmanProvider = WingmanProvider; /** * Hook to access the Wingman monitor instance and status */ function useWingman() { const context = (0, react_1.useContext)(WingmanContext); if (!context) { throw new Error('useWingman must be used within a WingmanProvider'); } return context; } /** * Hook to manually report errors to Wingman */ function useWingmanReporting() { const { monitor, isActive } = useWingman(); const reportError = react_1.default.useCallback((error, metadata) => { if (!isActive || !monitor) { console.warn('Wingman: Cannot report error - monitor not active'); return; } const errorObj = typeof error === 'string' ? new Error(error) : error; // Apply error filtering before reporting if ((0, errorFiltering_1.shouldReportError)(errorObj, metadata)) { monitor.reportCustomError(errorObj, metadata); } }, [monitor, isActive]); const reportInfo = react_1.default.useCallback((message, metadata) => { if (!isActive || !monitor) { console.warn('Wingman: Cannot report info - monitor not active'); return; } const infoError = new Error(message); const infoMetadata = { ...metadata, severity: 'low', type: 'Info' }; // Apply error filtering (info messages still go through the same filtering) if ((0, errorFiltering_1.shouldReportError)(infoError, infoMetadata)) { monitor.reportCustomError(infoError, infoMetadata); } }, [monitor, isActive]); return { reportError, reportInfo, isActive, }; } /** * Hook to access environment configuration and settings */ function useWingmanConfig() { const envConfig = (0, errorFiltering_1.getEnvironmentConfig)(); const { isActive } = useWingman(); return { ...envConfig, isActive, shouldReportErrors: envConfig.enableErrorReporting && isActive, }; } //# sourceMappingURL=provider.js.map