@critters/next
Version:
Secure bug reporting library for Next.js applications
153 lines (152 loc) • 6.78 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.withErrorReporting = withErrorReporting;
exports.withPageErrorReporting = withPageErrorReporting;
exports.withAppErrorReporting = withAppErrorReporting;
const reporter_1 = require("./reporter");
const react_1 = __importDefault(require("react"));
/**
* Create a middleware for Next.js API routes to catch and report errors
* @param handler The Next.js API route handler
* @param options Additional options for error reporting
* @returns A wrapped handler that reports errors
*/
function withErrorReporting(handler, options = {}) {
return (req, res) => __awaiter(this, void 0, void 0, function* () {
try {
// Call the original handler
return yield handler(req, res);
}
catch (error) {
// Prepare metadata for error reporting
const metadata = {
route: req.url,
method: req.method,
};
// Capture request data if enabled
if (options.captureRequest) {
metadata.query = req.query;
metadata.body = req.body;
metadata.headers = req.headers;
metadata.cookies = req.cookies;
}
// Report the error
yield reporter_1.Critters.getInstance().catch(error, metadata);
// Call the onError callback if provided
if (options.onError && error instanceof Error) {
options.onError(error, req, res);
}
// Re-throw the error to be handled by Next.js
throw error;
}
});
}
/**
* Create a middleware for Next.js pages to catch and report errors
* @param options Additional options for error reporting
* @returns A higher-order component that wraps the page component
*/
function withPageErrorReporting(options = {}) {
return function withErrorReportingHOC(PageComponent) {
// Define a new component that wraps the page component
const WrappedComponent = (props) => {
return react_1.default.createElement(PageComponent, props);
};
// Copy static methods and display name
WrappedComponent.displayName = `withErrorReporting(${PageComponent.displayName || PageComponent.name || "Page"})`;
// Handle getInitialProps if it exists
if ("getInitialProps" in PageComponent) {
WrappedComponent.getInitialProps = (ctx) => __awaiter(this, void 0, void 0, function* () {
try {
// Call the original getInitialProps
const initialProps = yield PageComponent.getInitialProps(ctx);
return initialProps;
}
catch (error) {
// Prepare metadata for error reporting
const metadata = {
page: ctx.pathname,
asPath: ctx.asPath,
query: ctx.query,
};
// Capture initial props context if enabled
if (options.captureInitialProps) {
metadata.initialPropsContext = {
req: ctx.req
? {
url: ctx.req.url,
method: ctx.req.method,
headers: ctx.req.headers,
}
: undefined,
res: ctx.res
? {
statusCode: ctx.res.statusCode,
}
: undefined,
};
}
// Report the error
yield reporter_1.Critters.getInstance().catch(error, metadata);
// Call the onError callback if provided
if (options.onError && error instanceof Error) {
options.onError(error, ctx);
}
// Re-throw the error
throw error;
}
});
}
return WrappedComponent;
};
}
/**
* Create a middleware for Next.js App Router to catch and report errors
* @param options Additional options for error reporting
* @returns A higher-order component that wraps the page component
*/
function withAppErrorReporting(options = {}) {
return function withAppErrorReportingHOC(AppComponent) {
// Define a new component that wraps the app component
function WrappedComponent(props) {
try {
return react_1.default.createElement(AppComponent, props);
}
catch (error) {
// Prepare metadata for error reporting
const metadata = {};
// Capture params if enabled
if (options.captureParams && props.params) {
metadata.params = props.params;
}
// Capture search params if enabled
if (options.captureSearchParams && props.searchParams) {
metadata.searchParams = props.searchParams;
}
// Report the error
reporter_1.Critters.getInstance().catch(error, metadata);
// Call the onError callback if provided
if (options.onError && error instanceof Error) {
options.onError(error, props);
}
// Re-throw the error to be handled by Next.js
throw error;
}
}
// Copy display name
WrappedComponent.displayName = `withAppErrorReporting(${AppComponent.displayName || AppComponent.name || "AppComponent"})`;
return WrappedComponent;
};
}