@critters/next
Version:
Secure bug reporting library for Next.js applications
223 lines (222 loc) • 7.17 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateSignature = generateSignature;
exports.isBrowser = isBrowser;
exports.isServer = isServer;
exports.getBrowserInfo = getBrowserInfo;
exports.formatError = formatError;
exports.safeStringify = safeStringify;
exports.sanitizeData = sanitizeData;
exports.getTimestamp = getTimestamp;
exports.debugLog = debugLog;
const crypto_1 = require("crypto");
const hmac_sha256_1 = __importDefault(require("crypto-js/hmac-sha256"));
const enc_hex_1 = __importDefault(require("crypto-js/enc-hex"));
/**
* Generate HMAC signature for request validation
*/
function generateSignature(payload, secretKey) {
// If no secret key is provided, return an empty string
if (!secretKey) {
return "";
}
// Use Node.js crypto in server environment
if (typeof window === "undefined" && typeof crypto_1.createHmac === "function") {
return (0, crypto_1.createHmac)("sha256", secretKey).update(payload).digest("hex");
}
// Use crypto-js in browser environment
return (0, hmac_sha256_1.default)(payload, secretKey).toString(enc_hex_1.default);
}
/**
* Check if code is running in browser environment
*/
function isBrowser() {
return typeof window !== "undefined";
}
/**
* Check if code is running in server environment
*/
function isServer() {
return !isBrowser();
}
/**
* Get browser information
*/
function getBrowserInfo() {
if (!isBrowser()) {
return {};
}
const { userAgent } = window.navigator;
// Extract browser name and version
const getBrowserDetails = () => {
var _a, _b, _c, _d, _e;
const ua = userAgent.toLowerCase();
let browser = "unknown";
let version = "unknown";
// Extract browser and version
if (ua.indexOf("edge") > -1) {
browser = "edge";
version = ((_a = ua.match(/edge\/(\d+(\.\d+)?)/)) === null || _a === void 0 ? void 0 : _a[1]) || "unknown";
}
else if (ua.indexOf("chrome") > -1) {
browser = "chrome";
version = ((_b = ua.match(/chrome\/(\d+(\.\d+)?)/)) === null || _b === void 0 ? void 0 : _b[1]) || "unknown";
}
else if (ua.indexOf("firefox") > -1) {
browser = "firefox";
version = ((_c = ua.match(/firefox\/(\d+(\.\d+)?)/)) === null || _c === void 0 ? void 0 : _c[1]) || "unknown";
}
else if (ua.indexOf("safari") > -1) {
browser = "safari";
version = ((_d = ua.match(/version\/(\d+(\.\d+)?)/)) === null || _d === void 0 ? void 0 : _d[1]) || "unknown";
}
else if (ua.indexOf("msie") > -1 || ua.indexOf("trident") > -1) {
browser = "ie";
version = ((_e = ua.match(/(?:msie |rv:)(\d+(\.\d+)?)/)) === null || _e === void 0 ? void 0 : _e[1]) || "unknown";
}
return { name: browser, version };
};
// Extract OS information
const getOSDetails = () => {
const ua = userAgent.toLowerCase();
let os = "unknown";
if (ua.indexOf("win") > -1) {
os = "windows";
}
else if (ua.indexOf("mac") > -1) {
os = "macos";
}
else if (ua.indexOf("linux") > -1) {
os = "linux";
}
else if (ua.indexOf("android") > -1) {
os = "android";
}
else if (ua.indexOf("ios") > -1 || /iphone|ipad|ipod/.test(ua)) {
os = "ios";
}
return os;
};
// Detect device type
const getDeviceType = () => {
const ua = userAgent.toLowerCase();
if (/ipad|tablet|playbook|silk/.test(ua)) {
return "tablet";
}
else if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/.test(ua)) {
return "mobile";
}
return "desktop";
};
// Get viewport dimensions
const getViewport = () => {
return {
width: window.innerWidth ||
document.documentElement.clientWidth ||
document.body.clientWidth,
height: window.innerHeight ||
document.documentElement.clientHeight ||
document.body.clientHeight,
};
};
const browserDetails = getBrowserDetails();
const os = getOSDetails();
const device = getDeviceType();
const viewport = getViewport();
return {
name: browserDetails.name,
version: browserDetails.version,
os,
device,
viewport,
userAgent,
};
}
/**
* Format error object into a standardized structure
*/
function formatError(error) {
if (error instanceof Error) {
return {
name: error.name,
message: error.message,
stack: error.stack,
};
}
// Handle non-Error objects
return {
name: "Unknown Error",
message: typeof error === "string" ? error : String(error),
};
}
/**
* Safe stringify function that handles circular references
*/
function safeStringify(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return "[Circular Reference]";
}
seen.add(value);
}
// Handle Error objects
if (value instanceof Error) {
return {
name: value.name,
message: value.message,
stack: value.stack,
};
}
return value;
});
}
/**
* Sanitize sensitive data from objects
*/
function sanitizeData(data, sensitiveKeys = ["password", "token", "secret", "key", "auth"]) {
const result = {};
for (const [key, value] of Object.entries(data)) {
// Check if the key contains any sensitive words
const isSensitive = sensitiveKeys.some((sensitiveKey) => key.toLowerCase().includes(sensitiveKey.toLowerCase()));
if (isSensitive) {
result[key] = "[REDACTED]";
}
else if (typeof value === "object" && value !== null) {
// Recursively sanitize nested objects
result[key] = Array.isArray(value)
? value.map((item) => typeof item === "object" && item !== null
? sanitizeData(item, sensitiveKeys)
: item)
: sanitizeData(value, sensitiveKeys);
}
else {
result[key] = value;
}
}
return result;
}
/**
* Get current timestamp in ISO format
*/
function getTimestamp() {
return new Date().toISOString();
}
/**
* Log message with prefix in debug mode
*/
function debugLog(message, data) {
try {
const config = require("./config").BugTrackerConfig.getInstance();
if (config.isDebugMode()) {
console.log(`[Bug Tracker] ${message}`, data);
}
}
catch (e) {
// Silently fail if config is not initialized
}
}