@sentry/remix
Version:
Official Sentry SDK for Remix
124 lines (103 loc) • 3.75 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const router = require('@remix-run/router');
const core = require('@sentry/core');
const debugBuild = require('../utils/debug-build.js');
const utils = require('../utils/utils.js');
const response = require('../utils/vendor/response.js');
/**
* Captures an exception happened in the Remix server.
*
* @param err The error to capture.
* @param name The name of the origin function.
* @param request The request object.
*
* @returns A promise that resolves when the exception is captured.
*/
async function captureRemixServerException(err, name, request) {
// Skip capturing if the thrown error is not a 5xx response
// https://remix.run/docs/en/main/route/loader#throwing-responses-in-loaders
if (router.isRouteErrorResponse(err) && err.status < 500) {
return;
}
if (response.isResponse(err) && err.status < 500) {
return;
}
// Skip capturing if the request is aborted as Remix docs suggest
// Ref: https://remix.run/docs/en/main/file-conventions/entry.server#handleerror
if (request.signal.aborted) {
debugBuild.DEBUG_BUILD && core.debug.warn('Skipping capture of aborted request');
return;
}
let normalizedRequest = {};
try {
normalizedRequest = core.winterCGRequestToRequestData(request);
} catch {
debugBuild.DEBUG_BUILD && core.debug.warn('Failed to normalize Remix request');
}
const objectifiedErr = core.objectify(err);
core.captureException(response.isResponse(objectifiedErr) ? await extractResponseError(objectifiedErr) : objectifiedErr, scope => {
scope.setSDKProcessingMetadata({ normalizedRequest });
scope.addEventProcessor(event => {
core.addExceptionMechanism(event, {
type: 'auto.function.remix.server',
handled: false,
data: {
function: name,
},
});
return event;
});
return scope;
});
}
/**
* Wraps the original `DataFunction` with error handling.
* This function also stores the form data keys if the action is being called.
*
* @param origFn The original `DataFunction`.
* @param name The name of the function.
* @param args The arguments of the function.
* @param span The span to store the form data keys.
*
* @returns The wrapped `DataFunction`.
*/
async function errorHandleDataFunction(
origFn,
name,
args,
span,
) {
return core.handleCallbackErrors(
async () => {
if (name === 'action' && span) {
const options = core.getClient()?.getOptions() ;
if (options?.sendDefaultPii && options.captureActionFormDataKeys) {
await utils.storeFormDataKeys(args, span, options.captureActionFormDataKeys);
}
}
return origFn.call(this, args);
},
err => {
// We capture all unexpected errors (except the `Route Error Response`s / Thrown Responses) in `handleError` function.
// This is both for consistency and also avoid duplicates such as primitives like `string` or `number` being captured twice.
if (response.isResponse(err)) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
captureRemixServerException(err, name, args.request);
}
throw err;
},
);
}
async function extractResponseError(response$1) {
const responseData = await response.extractData(response$1);
if (typeof responseData === 'string' && responseData.length > 0) {
return new Error(responseData);
}
if (response$1.statusText) {
return new Error(response$1.statusText);
}
return responseData;
}
exports.captureRemixServerException = captureRemixServerException;
exports.errorHandleDataFunction = errorHandleDataFunction;
//# sourceMappingURL=errors.js.map