unified-error-handling
Version:
A lightweight, zero-dependency error handling library with dynamic adapter loading for multiple error tracking services
1,089 lines (1,077 loc) • 33.5 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
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 __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/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, {
BaseAdapter: () => BaseAdapter,
CustomAdapter: () => CustomAdapter,
FirebaseAdapter: () => FirebaseAdapter,
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();
default:
return null;
}
}
var init_adapters = __esm({
"src/adapters/index.ts"() {
"use strict";
init_base_adapter();
init_sentry_adapter();
init_firebase_adapter();
init_custom_adapter();
}
});
// src/index.ts
var src_exports = {};
__export(src_exports, {
CustomAdapter: () => CustomAdapter,
FirebaseAdapter: () => FirebaseAdapter,
SentryAdapter: () => SentryAdapter,
addBreadcrumb: () => addBreadcrumb,
captureError: () => captureError,
captureMessage: () => captureMessage,
clearBreadcrumbs: () => clearBreadcrumbs,
createAdapter: () => createAdapter,
createCustomAdapter: () => createCustomAdapter,
errorStore: () => errorStore,
flush: () => flush,
initialize: () => initialize,
registerAdapter: () => registerAdapter,
removeAdapter: () => removeAdapter,
reset: () => reset,
setContext: () => setContext,
setUser: () => setUser,
subscribe: () => subscribe,
useAdapter: () => useAdapter
});
module.exports = __toCommonJS(src_exports);
// 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.innerHeight
}
}
});
}
}
captureError(error, additionalContext) {
if (!this.initialized) {
console.warn("[ErrorStore] Not initialized. Call initialize() first.");
return;
}
let normalizedError = this.normalizeError(error, additionalContext);
normalizedError = enrichError(normalizedError);
if (this.config.beforeSend) {
const processed = this.config.beforeSend(normalizedError);
if (!processed)
return;
}
if (this.offline && this.config.enableOfflineQueue) {
this.errorQueue.push(normalizedError);
return;
}
this.sendToAdapter(normalizedError);
this.listeners.forEach((listener) => listener(normalizedError));
}
captureMessage(message, level = "info") {
const error = new Error(message);
error.name = "CapturedMessage";
this.captureError(error, { extra: { level } });
}
setUser(user) {
var _a2;
if (user === null) {
delete this.context.user;
} else {
this.context.user = { ...user };
}
if (this.activeAdapter) {
const adapter = this.adapters.get(this.activeAdapter);
(_a2 = adapter == null ? void 0 : adapter.setContext) == null ? void 0 : _a2.call(adapter, this.context);
}
}
setContext(context) {
var _a2;
this.context = {
...this.context,
...context,
// Deep merge for nested objects
user: context.user ? { ...this.context.user, ...context.user } : this.context.user,
device: context.device ? { ...this.context.device, ...context.device } : this.context.device,
custom: context.custom ? { ...this.context.custom, ...context.custom } : this.context.custom,
tags: context.tags ? { ...this.context.tags, ...context.tags } : this.context.tags,
extra: context.extra ? { ...this.context.extra, ...context.extra } : this.context.extra
};
if (this.activeAdapter) {
const adapter = this.adapters.get(this.activeAdapter);
(_a2 = adapter == null ? void 0 : adapter.setContext) == null ? void 0 : _a2.call(adapter, this.context);
}
}
addBreadcrumb(breadcrumb) {
var _a2;
const fullBreadcrumb = {
...breadcrumb,
timestamp: Date.now()
};
this.breadcrumbs.push(fullBreadcrumb);
if (this.config.maxBreadcrumbs && this.breadcrumbs.length > this.config.maxBreadcrumbs) {
this.breadcrumbs = this.breadcrumbs.slice(-this.config.maxBreadcrumbs);
}
if (this.activeAdapter) {
const adapter = this.adapters.get(this.activeAdapter);
(_a2 = adapter == null ? void 0 : adapter.addBreadcrumb) == null ? void 0 : _a2.call(adapter, fullBreadcrumb);
}
}
clearBreadcrumbs() {
this.breadcrumbs = [];
}
async useAdapter(name, config) {
let adapter = this.adapters.get(name);
if (!adapter) {
adapter = await this.loadBuiltInAdapter(name) || void 0;
if (!adapter) {
throw new Error(`Adapter '${name}' not found. You may need to register a custom adapter.`);
}
}
await adapter.initialize(config);
this.adapters.set(name, adapter);
this.activeAdapter = name;
if (adapter.setContext) {
await adapter.setContext(this.context);
}
if (adapter.addBreadcrumb) {
for (const breadcrumb of this.breadcrumbs) {
await adapter.addBreadcrumb(breadcrumb);
}
}
if (this.errorQueue.length > 0) {
await this.processOfflineQueue();
}
}
removeAdapter(name) {
var _a2;
const adapter = this.adapters.get(name);
if (adapter) {
(_a2 = adapter.close) == null ? void 0 : _a2.call(adapter);
this.adapters.delete(name);
if (this.activeAdapter === name) {
this.activeAdapter = null;
}
}
}
async flush() {
var _a2;
if (this.activeAdapter) {
const adapter = this.adapters.get(this.activeAdapter);
await ((_a2 = adapter == null ? void 0 : adapter.flush) == null ? void 0 : _a2.call(adapter));
}
}
reset() {
this.adapters.forEach((adapter) => {
var _a2;
return (_a2 = adapter.close) == null ? void 0 : _a2.call(adapter);
});
this.context = {};
this.breadcrumbs = [];
this.adapters.clear();
this.activeAdapter = null;
this.errorQueue = [];
this.initialized = false;
if (this.globalHandlersInstalled) {
this.removeGlobalHandlers();
}
consoleInterceptor.disable();
networkInterceptor.disable();
}
// Public methods for advanced usage
subscribe(listener) {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
registerAdapter(name, adapter) {
this.adapters.set(name, adapter);
}
// Private methods
normalizeError(error, additionalContext) {
const errorObj = typeof error === "string" ? new Error(error) : error;
return {
message: errorObj.message || "Unknown error",
stack: errorObj.stack,
type: errorObj.name || "Error",
timestamp: Date.now(),
context: {
...this.context,
...additionalContext
},
breadcrumbs: [...this.breadcrumbs],
level: "error",
handled: true,
source: "manual"
};
}
async sendToAdapter(error) {
if (!this.activeAdapter) {
if (this.config.debug) {
console.error("[ErrorStore] No active adapter. Error:", error);
}
return;
}
const adapter = this.adapters.get(this.activeAdapter);
if (adapter) {
try {
await adapter.captureError(error);
} catch (err) {
console.error(`[ErrorStore] Failed to send error to adapter '${this.activeAdapter}':`, err);
}
}
}
installGlobalHandlers() {
if (this.globalHandlersInstalled)
return;
window.addEventListener("error", (event) => {
this.captureError(event.error || new Error(event.message), {
extra: {
source: "global",
filename: event.filename,
lineno: event.lineno,
colno: event.colno
}
});
});
window.addEventListener("unhandledrejection", (event) => {
const error = event.reason instanceof Error ? event.reason : new Error(String(event.reason));
this.captureError(error, {
extra: {
source: "unhandledRejection",
promise: event.promise
}
});
});
this.globalHandlersInstalled = true;
}
removeGlobalHandlers() {
this.globalHandlersInstalled = false;
}
setupOfflineHandlers() {
this.offline = !navigator.onLine;
this.offlineHandler = () => {
this.offline = true;
this.addBreadcrumb({
message: "Device went offline",
category: "network",
level: "warning"
});
};
this.onlineHandler = () => {
this.offline = false;
this.addBreadcrumb({
message: "Device came online",
category: "network",
level: "info"
});
if (this.errorQueue.length > 0) {
this.processOfflineQueue();
}
};
window.addEventListener("offline", this.offlineHandler);
window.addEventListener("online", this.onlineHandler);
}
async processOfflineQueue() {
const errors = [...this.errorQueue];
this.errorQueue = [];
for (const error of errors) {
await this.sendToAdapter(error);
}
}
async loadBuiltInAdapter(name) {
switch (name) {
case "console":
return {
name: "console",
async initialize() {
var _a2;
const originalLog = ((_a2 = consoleInterceptor["originalConsole"]) == null ? void 0 : _a2.log) || console.log;
originalLog("[ConsoleAdapter] Initialized");
},
async captureError(error) {
var _a2;
const originalError = ((_a2 = consoleInterceptor["originalConsole"]) == null ? void 0 : _a2.error) || console.error;
originalError("[Error]", error);
},
async captureMessage(message, level) {
var _a2;
const originalLog = ((_a2 = consoleInterceptor["originalConsole"]) == null ? void 0 : _a2.log) || console.log;
originalLog(`[${level || "info"}]`, message);
}
};
case "sentry":
case "firebase":
const { loadAdapter: loadAdapter2 } = await Promise.resolve().then(() => (init_adapters(), adapters_exports));
return await loadAdapter2(name);
default:
return null;
}
}
};
var errorStore = new ErrorStoreImpl();
var initialize = errorStore.initialize.bind(errorStore);
var captureError = errorStore.captureError.bind(errorStore);
var captureMessage = errorStore.captureMessage.bind(errorStore);
var setUser = errorStore.setUser.bind(errorStore);
var setContext = errorStore.setContext.bind(errorStore);
var addBreadcrumb = errorStore.addBreadcrumb.bind(errorStore);
var clearBreadcrumbs = errorStore.clearBreadcrumbs.bind(errorStore);
var useAdapter = errorStore.useAdapter.bind(errorStore);
var removeAdapter = errorStore.removeAdapter.bind(errorStore);
var flush = errorStore.flush.bind(errorStore);
var reset = errorStore.reset.bind(errorStore);
var subscribe = errorStore.subscribe.bind(errorStore);
var registerAdapter = errorStore.registerAdapter.bind(errorStore);
// src/index.ts
init_custom_adapter();
init_sentry_adapter();
init_firebase_adapter();
init_custom_adapter();
init_custom_adapter();
function createAdapter(name, config) {
const adapter = createCustomAdapter(config);
errorStore.registerAdapter(name, adapter);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CustomAdapter,
FirebaseAdapter,
SentryAdapter,
addBreadcrumb,
captureError,
captureMessage,
clearBreadcrumbs,
createAdapter,
createCustomAdapter,
errorStore,
flush,
initialize,
registerAdapter,
removeAdapter,
reset,
setContext,
setUser,
subscribe,
useAdapter
});