UNPKG

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
"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