unified-error-handling
Version:
A lightweight, zero-dependency error handling library with dynamic adapter loading for multiple error tracking services
1,531 lines (1,514 loc) • 83.8 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/adapters/base-adapter.ts
var BaseAdapter;
var init_base_adapter = __esm({
"src/adapters/base-adapter.ts"() {
"use strict";
BaseAdapter = class {
config;
sdkInstance;
sdkLoaded = false;
async initialize(config) {
this.config = config;
await this.loadSDK();
await this.initializeSDK();
}
async captureMessage(message, level) {
const error = {
message,
timestamp: Date.now(),
context: {},
breadcrumbs: [],
level: level || "info",
handled: true,
source: "manual"
};
await this.captureError(error);
}
async setContext(_context) {
}
async addBreadcrumb(_breadcrumb) {
}
async flush() {
}
async close() {
}
async dynamicImport(packageName) {
try {
return await import(packageName);
} catch (_error) {
throw new Error(
`Failed to load ${packageName}. Please install it:
npm install ${packageName}
or
yarn add ${packageName}`
);
}
}
};
}
});
// src/adapters/sentry-adapter.ts
var sentry_adapter_exports = {};
__export(sentry_adapter_exports, {
SentryAdapter: () => SentryAdapter
});
var SentryAdapter;
var init_sentry_adapter = __esm({
"src/adapters/sentry-adapter.ts"() {
"use strict";
init_base_adapter();
SentryAdapter = class extends BaseAdapter {
name = "sentry";
Sentry;
async loadSDK() {
if (this.sdkLoaded) return;
try {
const sentryModule = await this.dynamicImport("@sentry/browser");
this.Sentry = sentryModule;
this.sdkLoaded = true;
} catch (error) {
if (typeof window !== "undefined" && window.Sentry) {
this.Sentry = window.Sentry;
this.sdkLoaded = true;
} else {
throw error;
}
}
}
async initializeSDK() {
if (!this.config.dsn) {
throw new Error("Sentry DSN is required");
}
this.Sentry.init({
dsn: this.config.dsn,
environment: this.config.environment,
release: this.config.release,
debug: this.config.debug,
beforeSend: this.config.beforeSend,
// Disable Sentry's own global handlers since we handle them
integrations: this.config.integrations ? this.config.integrations : (integrations) => integrations.filter(
(integration) => !["GlobalHandlers", "TryCatch"].includes(integration.name)
)
});
}
async captureError(error) {
const sentryError = new Error(error.message);
if (error.stack) {
sentryError.stack = error.stack;
}
this.Sentry.captureException(sentryError, {
level: error.level,
contexts: {
custom: error.context.custom,
device: error.context.device
},
tags: error.context.tags,
extra: {
...error.context.extra,
fingerprint: error.fingerprint,
handled: error.handled,
source: error.source
},
user: error.context.user,
breadcrumbs: error.breadcrumbs.map((b) => ({
timestamp: b.timestamp / 1e3,
// Sentry expects seconds
message: b.message,
category: b.category,
level: b.level,
data: b.data
}))
});
}
async setContext(context) {
if (context.user) {
this.Sentry.setUser(context.user);
}
if (context.tags) {
Object.entries(context.tags).forEach(([key, value]) => {
this.Sentry.setTag(key, value);
});
}
if (context.extra) {
Object.entries(context.extra).forEach(([key, value]) => {
this.Sentry.setExtra(key, value);
});
}
if (context.custom) {
this.Sentry.setContext("custom", context.custom);
}
if (context.device) {
this.Sentry.setContext("device", context.device);
}
}
async addBreadcrumb(breadcrumb) {
this.Sentry.addBreadcrumb({
timestamp: breadcrumb.timestamp / 1e3,
message: breadcrumb.message,
category: breadcrumb.category,
level: breadcrumb.level,
data: breadcrumb.data
});
}
async flush() {
await this.Sentry.flush();
}
async close() {
await this.Sentry.close();
}
};
}
});
// src/adapters/firebase-adapter.ts
var firebase_adapter_exports = {};
__export(firebase_adapter_exports, {
FirebaseAdapter: () => FirebaseAdapter
});
var FirebaseAdapter;
var init_firebase_adapter = __esm({
"src/adapters/firebase-adapter.ts"() {
"use strict";
init_base_adapter();
FirebaseAdapter = class extends BaseAdapter {
name = "firebase";
crashlytics;
analytics;
async loadSDK() {
if (this.sdkLoaded) return;
try {
const { initializeApp, getApps } = await this.dynamicImport("firebase/app");
const { getCrashlytics } = await this.dynamicImport("firebase/crashlytics");
const { getAnalytics } = await this.dynamicImport("firebase/analytics");
let app;
if (getApps().length === 0) {
if (!this.config.firebaseConfig) {
throw new Error("Firebase configuration is required");
}
app = initializeApp(this.config.firebaseConfig);
} else {
app = getApps()[0];
}
this.crashlytics = getCrashlytics(app);
if (this.config.enableAnalytics !== false) {
try {
this.analytics = getAnalytics(app);
} catch (e) {
console.warn("Firebase Analytics not available:", e);
}
}
this.sdkLoaded = true;
} catch (_error) {
throw new Error(
`Failed to load Firebase SDK. Please install:
npm install firebase
or include Firebase SDK via CDN`
);
}
}
async initializeSDK() {
}
async captureError(error) {
const { recordException, log, setCustomKey } = await this.dynamicImport("firebase/crashlytics");
if (error.context.tags) {
for (const [key, value] of Object.entries(error.context.tags)) {
setCustomKey(this.crashlytics, key, value);
}
}
if (error.context.extra) {
for (const [key, value] of Object.entries(error.context.extra)) {
setCustomKey(this.crashlytics, key, JSON.stringify(value));
}
}
setCustomKey(this.crashlytics, "error_level", error.level || "error");
setCustomKey(this.crashlytics, "error_source", error.source || "unknown");
setCustomKey(this.crashlytics, "error_handled", String(error.handled));
for (const breadcrumb of error.breadcrumbs) {
log(this.crashlytics, `[${breadcrumb.category || "general"}] ${breadcrumb.message}`);
}
const errorObj = new Error(error.message);
if (error.stack) {
errorObj.stack = error.stack;
}
errorObj.name = error.type || "Error";
recordException(this.crashlytics, errorObj);
if (this.analytics) {
const { logEvent } = await this.dynamicImport("firebase/analytics");
logEvent(this.analytics, "exception", {
description: error.message,
fatal: error.level === "fatal"
});
}
}
async setContext(context) {
var _a2;
const { setUserId, setCustomKey } = await this.dynamicImport("firebase/crashlytics");
if ((_a2 = context.user) == null ? void 0 : _a2.id) {
setUserId(this.crashlytics, context.user.id);
}
if (context.user) {
for (const [key, value] of Object.entries(context.user)) {
if (key !== "id") {
setCustomKey(this.crashlytics, `user_${key}`, String(value));
}
}
}
if (context.device) {
for (const [key, value] of Object.entries(context.device)) {
setCustomKey(this.crashlytics, `device_${key}`, String(value));
}
}
if (context.custom) {
for (const [key, value] of Object.entries(context.custom)) {
setCustomKey(this.crashlytics, key, JSON.stringify(value));
}
}
}
async addBreadcrumb(breadcrumb) {
const { log } = await this.dynamicImport("firebase/crashlytics");
const level = breadcrumb.level || "info";
const category = breadcrumb.category || "general";
log(this.crashlytics, `[${level}][${category}] ${breadcrumb.message}`);
}
async flush() {
}
async close() {
}
};
}
});
// src/adapters/datadog-adapter.ts
var datadog_adapter_exports = {};
__export(datadog_adapter_exports, {
DataDogAdapter: () => DataDogAdapter
});
var DataDogAdapter;
var init_datadog_adapter = __esm({
"src/adapters/datadog-adapter.ts"() {
"use strict";
init_base_adapter();
DataDogAdapter = class extends BaseAdapter {
name = "datadog";
datadogRum;
datadogLogs;
async loadSDK() {
if (this.sdkLoaded) return;
try {
const ddRum = await this.dynamicImport("@datadog/browser-rum");
const ddLogs = await this.dynamicImport("@datadog/browser-logs");
this.datadogRum = ddRum.datadogRum;
this.datadogLogs = ddLogs.datadogLogs;
this.sdkLoaded = true;
} catch (_error) {
throw new Error(
`Failed to load DataDog SDK. Please install:
pnpm add @datadog/browser-rum @datadog/browser-logs
or include DataDog SDK via CDN`
);
}
}
async initializeSDK() {
if (!this.config.applicationId || !this.config.clientToken) {
throw new Error("DataDog requires applicationId and clientToken");
}
this.datadogRum.init({
applicationId: this.config.applicationId,
clientToken: this.config.clientToken,
site: this.config.site || "datadoghq.com",
service: this.config.service || "unified-error-handling",
env: this.config.environment || "production",
version: this.config.release || "1.0.0",
sessionSampleRate: this.config.sessionSampleRate ?? 100,
sessionReplaySampleRate: this.config.sessionReplaySampleRate ?? 0,
trackUserInteractions: this.config.trackInteractions ?? true,
trackResources: true,
trackLongTasks: true,
defaultPrivacyLevel: this.config.defaultPrivacyLevel || "mask-user-input"
});
this.datadogLogs.init({
clientToken: this.config.clientToken,
site: this.config.site || "datadoghq.com",
service: this.config.service || "unified-error-handling",
env: this.config.environment || "production",
forwardErrorsToLogs: true,
sessionSampleRate: 100
});
if (this.config.sessionReplaySampleRate && this.config.sessionReplaySampleRate > 0) {
this.datadogRum.startSessionReplayRecording();
}
}
async captureError(error) {
const errorObj = new Error(error.message);
if (error.stack) {
errorObj.stack = error.stack;
}
errorObj.name = error.type || "Error";
this.datadogRum.addError(errorObj, {
source: error.source || "custom",
level: error.level || "error",
handled: error.handled,
...error.context.extra
});
this.datadogLogs.logger.error(error.message, {
error: {
kind: error.type,
message: error.message,
stack: error.stack
},
...error.context.tags,
...error.context.extra
});
}
async setContext(context) {
if (context.user) {
this.datadogRum.setUser({
id: context.user.id,
email: context.user.email,
name: context.user.username,
...context.user
});
}
if (context.custom) {
for (const [key, value] of Object.entries(context.custom)) {
this.datadogRum.setGlobalContextProperty(key, value);
}
}
if (context.tags) {
for (const [key, value] of Object.entries(context.tags)) {
this.datadogRum.setGlobalContextProperty(key, value);
}
}
}
async addBreadcrumb(breadcrumb) {
this.datadogRum.addAction(breadcrumb.message, {
category: breadcrumb.category || "custom",
level: breadcrumb.level || "info",
...breadcrumb.data
});
}
async flush() {
}
async close() {
if (this.config.sessionReplaySampleRate && this.config.sessionReplaySampleRate > 0) {
this.datadogRum.stopSessionReplayRecording();
}
}
};
}
});
// src/adapters/bugsnag-adapter.ts
var bugsnag_adapter_exports = {};
__export(bugsnag_adapter_exports, {
BugsnagAdapter: () => BugsnagAdapter
});
var BugsnagAdapter;
var init_bugsnag_adapter = __esm({
"src/adapters/bugsnag-adapter.ts"() {
"use strict";
init_base_adapter();
BugsnagAdapter = class extends BaseAdapter {
name = "bugsnag";
bugsnag;
async loadSDK() {
if (this.sdkLoaded) return;
try {
const BugsnagModule = await this.dynamicImport("@bugsnag/js");
this.bugsnag = BugsnagModule.default || BugsnagModule;
this.sdkLoaded = true;
} catch (_error) {
throw new Error(
`Failed to load Bugsnag SDK. Please install:
pnpm add @bugsnag/js
or include Bugsnag SDK via CDN`
);
}
}
async initializeSDK() {
if (!this.config.apiKey) {
throw new Error("Bugsnag requires an apiKey");
}
this.bugsnag.start({
apiKey: this.config.apiKey,
appVersion: this.config.appVersion || this.config.release,
appType: this.config.appType || "browser",
releaseStage: this.config.environment || "production",
enabledReleaseStages: this.config.enabledReleaseStages || [
"development",
"staging",
"production"
],
autoDetectErrors: this.config.autoDetectErrors ?? true,
autoTrackSessions: this.config.autoSessionTracking ?? true,
maxBreadcrumbs: this.config.maxBreadcrumbs || 25,
enabledBreadcrumbTypes: this.config.enabledBreadcrumbTypes || [
"error",
"log",
"navigation",
"request",
"user"
],
redactedKeys: this.config.redactedKeys || ["password", "token", "secret", "apiKey"],
onError: (event) => {
if (this.config.context) {
event.addMetadata("custom", this.config.context);
}
if (this.config.tags) {
event.addMetadata("tags", this.config.tags);
}
return true;
}
});
}
async captureError(error) {
const errorObj = new Error(error.message);
if (error.stack) {
errorObj.stack = error.stack;
}
errorObj.name = error.type || "Error";
this.bugsnag.notify(errorObj, (event) => {
switch (error.level) {
case "fatal":
case "error":
event.severity = "error";
break;
case "warning":
event.severity = "warning";
break;
default:
event.severity = "info";
}
if (error.context.tags) {
event.addMetadata("tags", error.context.tags);
}
if (error.context.extra) {
event.addMetadata("extra", error.context.extra);
}
event.unhandled = !error.handled;
for (const breadcrumb of error.breadcrumbs) {
event.addMetadata("breadcrumbs", {
[breadcrumb.timestamp || Date.now()]: breadcrumb.message
});
}
});
}
async setContext(context) {
if (context.user) {
this.bugsnag.setUser(context.user.id, context.user.email, context.user.username);
}
if (context.custom) {
this.bugsnag.addMetadata("custom", context.custom);
}
if (context.device) {
this.bugsnag.addMetadata("device", context.device);
}
if (context.tags) {
this.bugsnag.addMetadata("tags", context.tags);
}
}
async addBreadcrumb(breadcrumb) {
this.bugsnag.leaveBreadcrumb(breadcrumb.message, breadcrumb.data || {}, breadcrumb.category);
}
async flush() {
}
async close() {
}
};
}
});
// src/adapters/rollbar-adapter.ts
var rollbar_adapter_exports = {};
__export(rollbar_adapter_exports, {
RollbarAdapter: () => RollbarAdapter
});
var RollbarAdapter;
var init_rollbar_adapter = __esm({
"src/adapters/rollbar-adapter.ts"() {
"use strict";
init_base_adapter();
RollbarAdapter = class extends BaseAdapter {
name = "rollbar";
rollbar;
async loadSDK() {
if (this.sdkLoaded) return;
try {
const RollbarModule = await this.dynamicImport("rollbar");
this.rollbar = RollbarModule.default || RollbarModule;
this.sdkLoaded = true;
} catch (_error) {
throw new Error(
`Failed to load Rollbar SDK. Please install:
pnpm add rollbar
or include Rollbar SDK via CDN`
);
}
}
async initializeSDK() {
if (!this.config.accessToken) {
throw new Error("Rollbar requires an accessToken");
}
this.rollbar = new this.rollbar({
accessToken: this.config.accessToken,
captureUncaught: this.config.captureUncaught ?? true,
captureUnhandledRejections: this.config.captureUnhandledRejections ?? true,
environment: this.config.environment || "production",
codeVersion: this.config.codeVersion || this.config.release,
autoInstrument: this.config.autoInstrument ?? true,
maxItems: this.config.maxItems || 5,
itemsPerMinute: this.config.itemsPerMinute || 60,
captureIp: this.config.captureIp ?? "anonymize",
captureEmail: this.config.captureEmail ?? false,
captureUsername: this.config.captureUsername ?? false,
verbose: this.config.verbose ?? false,
logLevel: this.config.logLevel || "debug",
payload: {
...this.config.payload,
client: {
javascript: {
source_map_enabled: true,
code_version: this.config.codeVersion || this.config.release
}
}
}
});
}
async captureError(error) {
const errorObj = new Error(error.message);
if (error.stack) {
errorObj.stack = error.stack;
}
errorObj.name = error.type || "Error";
let level;
switch (error.level) {
case "fatal":
level = "critical";
break;
case "error":
level = "error";
break;
case "warning":
level = "warning";
break;
case "info":
level = "info";
break;
default:
level = "debug";
}
const custom = {
source: error.source,
handled: error.handled,
...error.context.extra
};
if (error.context.tags) {
custom.tags = error.context.tags;
}
if (error.breadcrumbs.length > 0) {
custom.breadcrumbs = error.breadcrumbs.map((b) => ({
message: b.message,
category: b.category,
level: b.level,
timestamp: b.timestamp
}));
}
this.rollbar[level](errorObj, custom);
}
async setContext(context) {
if (context.user) {
this.rollbar.configure({
payload: {
person: {
id: context.user.id,
email: context.user.email,
username: context.user.username
}
}
});
}
if (context.custom) {
this.rollbar.configure({
payload: {
custom: context.custom
}
});
}
}
async addBreadcrumb(breadcrumb) {
this.rollbar.info(`[${breadcrumb.category || "breadcrumb"}] ${breadcrumb.message}`, {
breadcrumb: true,
...breadcrumb.data
});
}
async flush() {
if (this.rollbar && typeof this.rollbar.wait === "function") {
await new Promise((resolve) => {
this.rollbar.wait(resolve);
});
}
}
async close() {
}
};
}
});
// src/adapters/logrocket-adapter.ts
var logrocket_adapter_exports = {};
__export(logrocket_adapter_exports, {
LogRocketAdapter: () => LogRocketAdapter
});
var LogRocketAdapter;
var init_logrocket_adapter = __esm({
"src/adapters/logrocket-adapter.ts"() {
"use strict";
init_base_adapter();
LogRocketAdapter = class extends BaseAdapter {
name = "logrocket";
logRocket;
async loadSDK() {
if (this.sdkLoaded) return;
try {
const LogRocketModule = await this.dynamicImport("logrocket");
this.logRocket = LogRocketModule.default || LogRocketModule;
this.sdkLoaded = true;
} catch (_error) {
throw new Error(
`Failed to load LogRocket SDK. Please install:
pnpm add logrocket
or include LogRocket SDK via CDN`
);
}
}
async initializeSDK() {
if (!this.config.appId) {
throw new Error("LogRocket requires an appId");
}
this.logRocket.init(this.config.appId, {
release: this.config.release,
console: {
isEnabled: this.config.console ?? true
},
network: {
isEnabled: this.config.network ?? true,
requestSanitizer: this.config.requestSanitizer,
responseSanitizer: this.config.responseSanitizer
},
dom: {
isEnabled: this.config.dom ?? true,
inputSanitizer: this.config.inputSanitizer ?? true,
textSanitizer: this.config.textSanitizer ?? false
},
shouldCaptureIP: this.config.shouldCaptureIP ?? false,
rootHostname: this.config.rootHostname,
mergeIframes: this.config.mergeIframes ?? false
});
}
async captureError(error) {
const errorObj = new Error(error.message);
if (error.stack) {
errorObj.stack = error.stack;
}
errorObj.name = error.type || "Error";
this.logRocket.captureException(errorObj, {
tags: error.context.tags || {},
extra: {
level: error.level,
source: error.source,
handled: error.handled,
...error.context.extra
}
});
this.logRocket.track("Error", {
message: error.message,
type: error.type,
level: error.level,
source: error.source,
handled: error.handled
});
}
async setContext(context) {
if (context.user) {
this.logRocket.identify(context.user.id || "anonymous", {
email: context.user.email,
name: context.user.username,
...context.user
});
}
if (context.custom) {
for (const [key, value] of Object.entries(context.custom)) {
this.logRocket.track(key, value);
}
}
}
async addBreadcrumb(breadcrumb) {
this.logRocket.track(breadcrumb.category || "breadcrumb", {
message: breadcrumb.message,
level: breadcrumb.level,
...breadcrumb.data
});
}
async flush() {
}
async close() {
}
};
}
});
// src/adapters/raygun-adapter.ts
var raygun_adapter_exports = {};
__export(raygun_adapter_exports, {
RaygunAdapter: () => RaygunAdapter
});
var RaygunAdapter;
var init_raygun_adapter = __esm({
"src/adapters/raygun-adapter.ts"() {
"use strict";
init_base_adapter();
RaygunAdapter = class extends BaseAdapter {
name = "raygun";
raygun;
async loadSDK() {
if (this.sdkLoaded) return;
try {
const RaygunModule = await this.dynamicImport("raygun4js");
this.raygun = RaygunModule.default || RaygunModule;
this.sdkLoaded = true;
} catch (_error) {
throw new Error(
`Failed to load Raygun SDK. Please install:
pnpm add raygun4js
or include Raygun SDK via CDN`
);
}
}
async initializeSDK() {
if (!this.config.apiKey) {
throw new Error("Raygun requires an apiKey");
}
this.raygun("apiKey", this.config.apiKey);
this.raygun("options", {
ignoreAjaxAbort: this.config.ignoreAjaxAbort ?? true,
ignoreAjaxError: this.config.ignoreAjaxError ?? false,
debugMode: this.config.debug ?? false,
wrapAsynchronousCallbacks: true,
disableAnonymousUserTracking: this.config.disableAnonymousUserTracking ?? false,
disableErrorTracking: this.config.disableErrorTracking ?? false,
disablePulse: this.config.disablePulse ?? true,
pulseMaxVirtualPageDuration: 30 * 60 * 1e3,
// 30 minutes
pulseIgnoreUrlCasing: true
});
if (this.config.release) {
this.raygun("setVersion", this.config.release);
}
this.raygun("enableCrashReporting", this.config.enableCrashReporting ?? true);
if (!this.config.disablePulse) {
this.raygun("enablePulse", true);
}
if (this.config.withTags && this.config.withTags.length > 0) {
this.raygun("withTags", this.config.withTags);
}
if (this.config.customData) {
this.raygun("withCustomData", this.config.customData);
}
}
async captureError(error) {
const errorObj = new Error(error.message);
if (error.stack) {
errorObj.stack = error.stack;
}
errorObj.name = error.type || "Error";
const customData = {
level: error.level,
source: error.source,
handled: error.handled,
breadcrumbs: error.breadcrumbs.map((b) => ({
message: b.message,
category: b.category,
timestamp: b.timestamp
})),
...error.context.extra
};
const tags = [];
if (error.context.tags) {
for (const [key, value] of Object.entries(error.context.tags)) {
tags.push(`${key}:${value}`);
}
}
tags.push(`level:${error.level}`);
tags.push(`handled:${error.handled}`);
this.raygun("send", errorObj, customData, tags);
}
async setContext(context) {
if (context.user) {
this.raygun("setUser", {
identifier: context.user.id,
email: context.user.email,
fullName: context.user.username,
isAnonymous: !context.user.id
});
}
if (context.custom) {
this.raygun("withCustomData", context.custom);
}
if (context.tags) {
const tags = Object.entries(context.tags).map(([key, value]) => `${key}:${value}`);
this.raygun("withTags", tags);
}
}
async addBreadcrumb(breadcrumb) {
this.raygun("recordBreadcrumb", breadcrumb.message, {
category: breadcrumb.category,
level: breadcrumb.level,
...breadcrumb.data
});
}
async flush() {
}
async close() {
this.raygun("enableCrashReporting", false);
this.raygun("enablePulse", false);
}
};
}
});
// src/adapters/appcenter-adapter.ts
var appcenter_adapter_exports = {};
__export(appcenter_adapter_exports, {
AppCenterAdapter: () => AppCenterAdapter
});
var AppCenterAdapter;
var init_appcenter_adapter = __esm({
"src/adapters/appcenter-adapter.ts"() {
"use strict";
init_base_adapter();
AppCenterAdapter = class extends BaseAdapter {
name = "appcenter";
crashes;
analytics;
async loadSDK() {
if (this.sdkLoaded) return;
try {
const AppCenterCrashes = await this.dynamicImport("appcenter-crashes");
const AppCenterAnalytics = await this.dynamicImport("appcenter-analytics");
this.crashes = AppCenterCrashes.default || AppCenterCrashes;
this.analytics = AppCenterAnalytics.default || AppCenterAnalytics;
this.sdkLoaded = true;
} catch (_error) {
throw new Error(
`Failed to load AppCenter SDK. Please install:
pnpm add appcenter-crashes appcenter-analytics
Note: AppCenter is primarily for React Native/mobile apps.
For web apps, consider using a different adapter.`
);
}
}
async initializeSDK() {
if (!this.config.appSecret) {
throw new Error("AppCenter requires an appSecret");
}
if (this.crashes && typeof this.crashes.setEnabled === "function") {
await this.crashes.setEnabled(true);
}
if (this.analytics && typeof this.analytics.setEnabled === "function") {
await this.analytics.setEnabled(true);
}
if (this.config.userId && this.crashes && typeof this.crashes.setUserId === "function") {
await this.crashes.setUserId(this.config.userId);
}
}
async captureError(error) {
const errorObj = new Error(error.message);
if (error.stack) {
errorObj.stack = error.stack;
}
errorObj.name = error.type || "Error";
const properties = {
level: error.level || "error",
source: error.source || "unknown",
handled: String(error.handled)
};
if (error.context.tags) {
for (const [key, value] of Object.entries(error.context.tags)) {
properties[key] = String(value);
}
}
if (error.context.extra) {
for (const [key, value] of Object.entries(error.context.extra)) {
properties[`extra_${key}`] = typeof value === "string" ? value : JSON.stringify(value);
}
}
if (this.crashes && typeof this.crashes.trackError === "function") {
await this.crashes.trackError(errorObj, properties);
}
if (this.analytics && typeof this.analytics.trackEvent === "function") {
await this.analytics.trackEvent("Error", {
message: error.message.substring(0, 256),
// AppCenter has property length limits
type: error.type || "Error",
...properties
});
}
}
async setContext(context) {
var _a2;
if (((_a2 = context.user) == null ? void 0 : _a2.id) && this.crashes && typeof this.crashes.setUserId === "function") {
await this.crashes.setUserId(context.user.id);
}
if (context.user && this.analytics && typeof this.analytics.trackEvent === "function") {
await this.analytics.trackEvent("UserIdentified", {
userId: context.user.id || "anonymous",
email: context.user.email || "",
username: context.user.username || ""
});
}
}
async addBreadcrumb(breadcrumb) {
if (this.analytics && typeof this.analytics.trackEvent === "function") {
await this.analytics.trackEvent("Breadcrumb", {
message: breadcrumb.message.substring(0, 256),
category: breadcrumb.category || "custom",
level: breadcrumb.level || "info"
});
}
}
async flush() {
}
async close() {
if (this.crashes && typeof this.crashes.setEnabled === "function") {
await this.crashes.setEnabled(false);
}
if (this.analytics && typeof this.analytics.setEnabled === "function") {
await this.analytics.setEnabled(false);
}
}
};
}
});
// src/adapters/custom-adapter.ts
function createCustomAdapter(config) {
return new CustomAdapter(config);
}
var CustomAdapter;
var init_custom_adapter = __esm({
"src/adapters/custom-adapter.ts"() {
"use strict";
CustomAdapter = class {
name = "custom";
config;
context = {};
breadcrumbs = [];
constructor(config) {
this.config = config;
}
async initialize() {
if (this.config.initialize) {
await this.config.initialize();
}
}
async captureError(error) {
const fullContext = {
...this.context,
...error.context
};
await this.config.send(error, fullContext);
}
async captureMessage(message, level) {
const error = {
message,
timestamp: Date.now(),
context: this.context,
breadcrumbs: [...this.breadcrumbs],
level: level || "info",
handled: true,
source: "manual"
};
await this.captureError(error);
}
async setContext(context) {
this.context = { ...context };
if (this.config.setContext) {
await this.config.setContext(context);
}
}
async addBreadcrumb(breadcrumb) {
this.breadcrumbs.push(breadcrumb);
if (this.config.addBreadcrumb) {
await this.config.addBreadcrumb(breadcrumb);
}
}
async flush() {
if (this.config.flush) {
await this.config.flush();
}
}
async close() {
if (this.config.close) {
await this.config.close();
}
}
};
}
});
// src/adapters/index.ts
var adapters_exports = {};
__export(adapters_exports, {
AppCenterAdapter: () => AppCenterAdapter,
BaseAdapter: () => BaseAdapter,
BugsnagAdapter: () => BugsnagAdapter,
CustomAdapter: () => CustomAdapter,
DataDogAdapter: () => DataDogAdapter,
FirebaseAdapter: () => FirebaseAdapter,
LogRocketAdapter: () => LogRocketAdapter,
RaygunAdapter: () => RaygunAdapter,
RollbarAdapter: () => RollbarAdapter,
SentryAdapter: () => SentryAdapter,
createCustomAdapter: () => createCustomAdapter,
loadAdapter: () => loadAdapter
});
async function loadAdapter(name) {
switch (name) {
case "sentry": {
const { SentryAdapter: SentryAdapter2 } = await Promise.resolve().then(() => (init_sentry_adapter(), sentry_adapter_exports));
return new SentryAdapter2();
}
case "firebase": {
const { FirebaseAdapter: FirebaseAdapter2 } = await Promise.resolve().then(() => (init_firebase_adapter(), firebase_adapter_exports));
return new FirebaseAdapter2();
}
case "datadog": {
const { DataDogAdapter: DataDogAdapter2 } = await Promise.resolve().then(() => (init_datadog_adapter(), datadog_adapter_exports));
return new DataDogAdapter2();
}
case "bugsnag": {
const { BugsnagAdapter: BugsnagAdapter2 } = await Promise.resolve().then(() => (init_bugsnag_adapter(), bugsnag_adapter_exports));
return new BugsnagAdapter2();
}
case "rollbar": {
const { RollbarAdapter: RollbarAdapter2 } = await Promise.resolve().then(() => (init_rollbar_adapter(), rollbar_adapter_exports));
return new RollbarAdapter2();
}
case "logrocket": {
const { LogRocketAdapter: LogRocketAdapter2 } = await Promise.resolve().then(() => (init_logrocket_adapter(), logrocket_adapter_exports));
return new LogRocketAdapter2();
}
case "raygun": {
const { RaygunAdapter: RaygunAdapter2 } = await Promise.resolve().then(() => (init_raygun_adapter(), raygun_adapter_exports));
return new RaygunAdapter2();
}
case "appcenter": {
const { AppCenterAdapter: AppCenterAdapter2 } = await Promise.resolve().then(() => (init_appcenter_adapter(), appcenter_adapter_exports));
return new AppCenterAdapter2();
}
default:
return null;
}
}
var init_adapters = __esm({
"src/adapters/index.ts"() {
"use strict";
init_base_adapter();
init_sentry_adapter();
init_firebase_adapter();
init_datadog_adapter();
init_bugsnag_adapter();
init_rollbar_adapter();
init_logrocket_adapter();
init_raygun_adapter();
init_appcenter_adapter();
init_custom_adapter();
}
});
// src/react/index.ts
var index_exports = {};
__export(index_exports, {
ErrorBoundary: () => ErrorBoundary,
createErrorHandlingHOC: () => createErrorHandlingHOC,
useAsyncError: () => useAsyncError,
useAsyncOperation: () => useAsyncOperation,
useComponentError: () => useComponentError,
useErrorHandler: () => useErrorHandler,
useErrorStore: () => useErrorStore,
useErrorTracking: () => useErrorTracking,
useExtendedErrorHandler: () => useExtendedErrorHandler,
usePerformanceMonitor: () => usePerformanceMonitor,
withApiErrorHandling: () => withApiErrorHandling,
withAsyncErrorHandler: () => withAsyncErrorHandler,
withCompleteErrorHandling: () => withCompleteErrorHandling,
withCriticalErrorHandling: () => withCriticalErrorHandling,
withErrorBoundary: () => withErrorBoundary,
withErrorHandler: () => withErrorHandler,
withErrorHandling: () => withErrorHandling,
withFormErrorHandling: () => withFormErrorHandling,
withPageErrorHandling: () => withPageErrorHandling
});
module.exports = __toCommonJS(index_exports);
// src/react/hooks.ts
var import_react = require("react");
// src/utils/error-enricher.ts
function enrichError(error) {
if (typeof window !== "undefined") {
error.context.device = {
...error.context.device,
userAgent: navigator.userAgent,
language: navigator.language,
platform: navigator.platform,
cookieEnabled: navigator.cookieEnabled,
onLine: navigator.onLine,
viewport: {
width: window.innerWidth,
height: window.innerHeight
},
screen: {
width: screen.width,
height: screen.height,
colorDepth: screen.colorDepth
}
};
error.context.extra = {
...error.context.extra,
url: window.location.href,
referrer: document.referrer
};
}
if (!error.fingerprint) {
error.fingerprint = generateFingerprint(error);
}
if (error.stack) {
const parsedStack = parseStackTrace(error.stack);
error.context.extra = {
...error.context.extra,
parsedStack
};
}
return error;
}
function generateFingerprint(error) {
const parts = [
error.type || "Error",
error.message,
error.stack ? extractTopStackFrame(error.stack) : ""
].filter(Boolean);
return parts.join("|").replace(/\s+/g, " ").trim();
}
function extractTopStackFrame(stack) {
const lines = stack.split("\n");
for (const line of lines) {
if (line.includes("at ") && !line.includes("Error")) {
return line.trim();
}
}
return "";
}
function parseStackTrace(stack) {
const frames = [];
const lines = stack.split("\n");
for (const line of lines) {
if (!line.includes("at ")) continue;
const functionMatch = line.match(/at\s+([^\s]+)\s+\(/);
const functionName = functionMatch ? functionMatch[1] : "<anonymous>";
const fileMatch = line.match(/\((.+):(\d+):(\d+)\)/);
if (fileMatch) {
frames.push({
function: functionName,
file: fileMatch[1],
line: parseInt(fileMatch[2], 10),
column: parseInt(fileMatch[3], 10)
});
} else {
const altMatch = line.match(/at\s+(.+):(\d+):(\d+)/);
if (altMatch) {
frames.push({
file: altMatch[1],
line: parseInt(altMatch[2], 10),
column: parseInt(altMatch[3], 10)
});
}
}
}
return frames;
}
// src/utils/console-interceptor.ts
var ConsoleInterceptor = class {
originalConsole = {
error: console.error,
warn: console.warn,
log: console.log,
info: console.info
};
enabled = false;
enable() {
if (this.enabled) return;
console.error = (...args) => {
this.originalConsole.error.apply(console, args);
const error = args[0] instanceof Error ? args[0] : new Error(String(args[0]));
errorStore.captureError(error, {
extra: {
source: "console.error",
consoleArgs: args.slice(1)
}
});
errorStore.addBreadcrumb({
message: `console.error: ${args.map((arg) => String(arg)).join(" ")}`,
category: "console",
level: "error"
});
};
console.warn = (...args) => {
this.originalConsole.warn.apply(console, args);
errorStore.addBreadcrumb({
message: `console.warn: ${args.map((arg) => String(arg)).join(" ")}`,
category: "console",
level: "warning"
});
};
console.log = (...args) => {
this.originalConsole.log.apply(console, args);
errorStore.addBreadcrumb({
message: `console.log: ${args.map((arg) => String(arg)).join(" ")}`,
category: "console",
level: "info"
});
};
console.info = (...args) => {
this.originalConsole.info.apply(console, args);
errorStore.addBreadcrumb({
message: `console.info: ${args.map((arg) => String(arg)).join(" ")}`,
category: "console",
level: "info"
});
};
this.enabled = true;
}
disable() {
if (!this.enabled) return;
console.error = this.originalConsole.error;
console.warn = this.originalConsole.warn;
console.log = this.originalConsole.log;
console.info = this.originalConsole.info;
this.enabled = false;
}
};
var consoleInterceptor = new ConsoleInterceptor();
// src/utils/network-interceptor.ts
var _a, _b;
var NetworkInterceptor = class {
enabled = false;
originalFetch = typeof window !== "undefined" ? window.fetch : void 0;
xhrPrototype = typeof XMLHttpRequest !== "undefined" ? XMLHttpRequest.prototype : void 0;
originalOpen = (_a = this.xhrPrototype) == null ? void 0 : _a.open;
originalSend = (_b = this.xhrPrototype) == null ? void 0 : _b.send;
enable() {
if (this.enabled || typeof window === "undefined" || !this.originalFetch || !this.xhrPrototype) return;
window.fetch = async (...args) => {
const [input, init] = args;
const url = typeof input === "string" ? input : input instanceof Request ? input.url : input.toString();
const method = (init == null ? void 0 : init.method) || "GET";
const startTime = Date.now();
try {
const response = await this.originalFetch.apply(window, args);
const duration = Date.now() - startTime;
errorStore.addBreadcrumb({
message: `${method} ${url} (${response.status})`,
category: "fetch",
level: response.ok ? "info" : "warning",
data: {
method,
url,
status: response.status,
statusText: response.statusText,
duration
}
});
if (!response.ok && response.status >= 400) {
const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
errorStore.captureError(error, {
extra: {
source: "fetch",
url,
method,
status: response.status,
statusText: response.statusText,
duration
}
});
}
return response;
} catch (error) {
const duration = Date.now() - startTime;
errorStore.addBreadcrumb({
message: `${method} ${url} (failed)`,
category: "fetch",
level: "error",
data: {
method,
url,
error: String(error),
duration
}
});
errorStore.captureError(error, {
extra: {
source: "fetch",
url,
method,
duration
}
});
throw error;
}
};
const self = this;
this.xhrPrototype.open = function(method, url, ...args) {
this._method = method;
this._url = url;
this._startTime = Date.now();
return self.originalOpen.apply(this, [method, url, ...args]);
};
this.xhrPrototype.send = function(...args) {
const xhr = this;
xhr.addEventListener("load", function() {
const duration = Date.now() - (xhr._startTime || 0);
errorStore.addBreadcrumb({
message: `${xhr._method} ${xhr._url} (${xhr.status})`,
category: "xhr",
level: xhr.status >= 200 && xhr.status < 400 ? "info" : "warning",
data: {
method: xhr._method,
url: xhr._url,
status: xhr.status,
statusText: xhr.statusText,
duration
}
});
if (xhr.status >= 400) {
const error = new Error(`HTTP ${xhr.status}: ${xhr.statusText}`);
errorStore.captureError(error, {
extra: {
source: "xhr",
url: xhr._url,
method: xhr._method,
status: xhr.status,
statusText: xhr.statusText,
duration
}
});
}
});
xhr.addEventListener("error", function() {
const duration = Date.now() - (xhr._startTime || 0);
errorStore.addBreadcrumb({
message: `${xhr._method} ${xhr._url} (failed)`,
category: "xhr",
level: "error",
data: {
method: xhr._method,
url: xhr._url,
duration
}
});
const error = new Error(`XHR failed: ${xhr._method} ${xhr._url}`);
errorStore.captureError(error, {
extra: {
source: "xhr",
url: xhr._url,
method: xhr._method,
duration
}
});
});
return self.originalSend.apply(this, args);
};
this.enabled = true;
}
disable() {
if (!this.enabled || typeof window === "undefined" || !this.originalFetch || !this.xhrPrototype) return;
window.fetch = this.originalFetch;
if (this.originalOpen) this.xhrPrototype.open = this.originalOpen;
if (this.originalSend) this.xhrPrototype.send = this.originalSend;
this.enabled = false;
}
};
var networkInterceptor = new NetworkInterceptor();
// src/store/error-store.ts
var ErrorStoreImpl = class {
// State
context = {};
breadcrumbs = [];
adapters = /* @__PURE__ */ new Map();
activeAdapter = null;
config = {
maxBreadcrumbs: 100,
enableGlobalHandlers: true,
enableOfflineQueue: true,
enableConsoleCapture: true,
enableNetworkCapture: false,
debug: false
};
initialized = false;
offline = false;
errorQueue = [];
// Private properties
listeners = /* @__PURE__ */ new Set();
globalHandlersInstalled = false;
offlineHandler;
onlineHandler;
constructor() {
if (typeof window !== "undefined") {
this.setupOfflineHandlers();
}
}
// Actions
initialize(config) {
if (this.initialized) {
console.warn("[ErrorStore] Already initialized");
return;
}
this.config = { ...this.config, ...config };
this.initialized = true;
if (this.config.enableGlobalHandlers && typeof window !== "undefined") {
this.installGlobalHandlers();
}
if (this.config.enableConsoleCapture) {
consoleInterceptor.enable();
}
if (this.config.enableNetworkCapture) {
networkInterceptor.enable();
}
if (typeof window !== "undefined") {
this.setContext({
device: {
platform: "web",
userAgent: navigator.userAgent,
language: navigator.language,
viewport: {
width: window.innerWidth,
height: window.inn