@sentry/remix
Version:
Official Sentry SDK for Remix
84 lines (81 loc) • 3.33 kB
JavaScript
import { createHash } from 'node:crypto';
const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const DEBUG_ID_IDENTIFIER_REGEX = /sentry-dbid-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/;
const INLINE_SOURCE_MAP_REGEX = /\n?\/\/# sourceMappingURL=data:application\/json;base64,([A-Za-z0-9+/=]+)\s*$/;
function getDebugId(source) {
const hash = createHash("sha256").update(source).digest("hex");
const variant = ["8", "9", "a", "b"][hash.charCodeAt(16) % 4];
return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-4${hash.slice(13, 16)}-${variant}${hash.slice(17, 20)}-${hash.slice(20, 32)}`;
}
function getDebugIdSnippet(debugId) {
return `!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{};var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="${debugId}",e._sentryDebugIdIdentifier="sentry-dbid-${debugId}");}catch(e){}}();`;
}
function findDebugId(code) {
return code.match(DEBUG_ID_IDENTIFIER_REGEX)?.[1];
}
function injectDebugIdSnippet(source, debugId) {
const snippet = getDebugIdSnippet(debugId);
const inlineSourceMap = source.match(INLINE_SOURCE_MAP_REGEX);
if (!inlineSourceMap?.[1]) {
return `${snippet}
${source}`;
}
const code = source.slice(0, inlineSourceMap.index);
const map = JSON.parse(Buffer.from(inlineSourceMap[1], "base64").toString("utf8"));
const shiftedMap = {
version: 3,
sources: map.sources?.slice(0, 1) ?? [],
names: [],
mappings: getShiftedIdentityMappings(map.mappings)
};
return `${snippet}
${code}
//# sourceMappingURL=data:application/json;base64,${Buffer.from(JSON.stringify(shiftedMap)).toString("base64")}`;
}
function addDebugIdToSourceMap(sourceMap, debugId) {
const map = JSON.parse(sourceMap);
map.debug_id = debugId;
map.debugId = debugId;
return JSON.stringify(map);
}
function getShiftedIdentityMappings(mappings) {
let previousLine = 0;
let previousColumn = 0;
const lines = mappings.split(";").map((line, lineIndex) => {
let column = 0;
let previousGeneratedColumn = 0;
return line.split(",").filter(Boolean).map((segment) => {
column += decodeFirstVlq(segment);
const encoded = `${encodeVlq(column - previousGeneratedColumn)}A${encodeVlq(lineIndex - previousLine)}${encodeVlq(column - previousColumn)}`;
previousGeneratedColumn = column;
previousLine = lineIndex;
previousColumn = column;
return encoded;
}).join(",");
});
return `;${lines.join(";")}`;
}
function decodeFirstVlq(segment) {
let value = 0;
let factor = 1;
let index = 0;
let digit;
do {
digit = BASE64_CHARS.indexOf(segment.charAt(index++));
value += digit % 32 * factor;
factor *= 32;
} while (digit >= 32);
return value % 2 ? -(value - 1) / 2 : value / 2;
}
function encodeVlq(value) {
let vlq = value < 0 ? -value * 2 + 1 : value * 2;
let encoded = "";
do {
const digit = vlq % 32;
vlq = Math.floor(vlq / 32);
encoded += BASE64_CHARS.charAt(vlq > 0 ? digit + 32 : digit);
} while (vlq > 0);
return encoded;
}
export { addDebugIdToSourceMap, findDebugId, getDebugId, getDebugIdSnippet, injectDebugIdSnippet };
//# sourceMappingURL=debugId.js.map