wingman-monitor
Version:
Runtime error monitoring package with React provider that automatically reports errors to webhook endpoints
241 lines • 10.6 kB
JavaScript
;
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