UNPKG

@shirudo/base-error

Version:

A simple error base class for TypeScript

252 lines (249 loc) 9.75 kB
var __typeError = (msg) => { throw TypeError(msg); }; var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); // src/BaseError.ts var _BaseError_instances, setCause_fn, serializeCause_fn, serializeCircularObject_fn, _BaseError_static, hasNativeCauseSupport_fn, captureStack_fn, filterInternalFrames_fn; var _BaseError = class _BaseError extends Error { /** * Creates a new BaseError instance with automatic name inference. * * @param message – Human-readable explanation (name will be inferred from constructor) * @param cause – Optional underlying error or extra context */ constructor(message, cause) { super(message); __privateAdd(this, _BaseError_instances); /** Epoch-ms timestamp (numeric) */ this.timestamp = Date.now(); /** ISO-8601 timestamp (string) for log aggregators that prefer text */ this.timestampIso = (/* @__PURE__ */ new Date()).toISOString(); this._localizedMessages = /* @__PURE__ */ new Map(); this.name = this.constructor.name; if (cause !== void 0) { __privateMethod(this, _BaseError_instances, setCause_fn).call(this, cause); } Object.setPrototypeOf(this, new.target.prototype); this.stack = __privateMethod(this, _BaseError_instances, captureStack_fn).call(this); } // ———————————————————————————————————————————————————————————————— // Methods for User-Friendly Messages // ———————————————————————————————————————————————————————————————— /** * Sets the default user-friendly message. * This is used as a fallback when a specific localization is not available. * @param message The default user-friendly message (typically in English). * @returns The error instance for chaining. */ withUserMessage(message) { this._defaultUserMessage = message; return this; } /** * Adds a user-friendly message for a specific language. * Throws an error if a message for the given language already exists. * @param lang The language code (e.g., 'de', 'es', 'fr-CA'). * @param message The localized message. * @returns The error instance for chaining. * @throws Error if a message for the given language already exists. */ addLocalizedMessage(lang, message) { if (this._localizedMessages.has(lang)) { throw new Error( `Localized message for language '${lang}' already exists. Use updateLocalizedMessage() to modify existing messages.` ); } this._localizedMessages.set(lang, message); return this; } /** * Updates or sets a user-friendly message for a specific language. * This method allows overwriting existing messages for the same language. * @param lang The language code (e.g., 'de', 'es', 'fr-CA'). * @param message The localized message. * @returns The error instance for chaining. */ updateLocalizedMessage(lang, message) { this._localizedMessages.set(lang, message); return this; } /** * Retrieves the most appropriate user-friendly message based on language preference. * The fallback order is: preferred language -> fallback language -> default message. * @param options - Language preference options. * @returns The user-friendly message, or `undefined` if none is set. */ getUserMessage(options) { const { preferredLang, fallbackLang } = options || {}; if (preferredLang && this._localizedMessages.has(preferredLang)) { return this._localizedMessages.get(preferredLang); } if (fallbackLang && this._localizedMessages.has(fallbackLang)) { return this._localizedMessages.get(fallbackLang); } return this._defaultUserMessage; } /** Serialises the error for JSON logs */ toJSON() { const { name, message, timestamp, timestampIso, stack } = this; const cause = this.cause; const json = { name, message, // The original technical message timestamp, timestampIso, stack, cause: __privateMethod(this, _BaseError_instances, serializeCause_fn).call(this, cause) }; if (this._defaultUserMessage) { json.userMessage = this._defaultUserMessage; } if (this._localizedMessages.size > 0) { json.localizedMessages = Object.fromEntries(this._localizedMessages); } return json; } /** Readable one-liner plus optional nested cause. */ toString() { const cause = this.cause; return `[${this.name}] ${this.message}${cause ? ` Caused by: ${cause}` : ""}`; } }; _BaseError_instances = new WeakSet(); // ———————————————————————————————————————————————————————————————— // Internal helpers // ———————————————————————————————————————————————————————————————— /** * Sets the cause property using native support when available, with fallback. * This provides better compatibility across different JavaScript environments. */ setCause_fn = function(cause) { var _a; if (__privateMethod(_a = _BaseError, _BaseError_static, hasNativeCauseSupport_fn).call(_a)) { try { Object.defineProperty(this, "cause", { value: cause, configurable: true, writable: true, enumerable: false // Keep it non-enumerable like native cause }); } catch { this.cause = cause; } } else { try { Object.defineProperty(this, "cause", { value: cause, configurable: true, writable: true, enumerable: false }); } catch { this.cause = cause; } } }; /** * Intelligently serializes the cause for JSON output. * Preserves stack traces and nested data instead of just toString(). */ serializeCause_fn = function(cause) { if (cause === void 0 || cause === null) { return cause; } if (cause instanceof Error) { return { name: cause.name, message: cause.message, stack: cause.stack, // Recursively serialize nested causes cause: __privateMethod(this, _BaseError_instances, serializeCause_fn).call(this, cause.cause) }; } if (typeof cause === "object" && cause !== null) { try { return JSON.parse(JSON.stringify(cause)); } catch { return __privateMethod(this, _BaseError_instances, serializeCircularObject_fn).call(this, cause); } } return cause; }; /** * Creates a more useful representation of circular objects for debugging. * Instead of just "[object Object]", it extracts key information. */ serializeCircularObject_fn = function(obj) { const type = obj.constructor?.name || "Object"; const keys = Object.keys(obj).slice(0, 5); const keyInfo = keys.length > 0 ? ` with keys: [${keys.join(", ")}]` : ""; const moreKeys = Object.keys(obj).length > 5 ? "..." : ""; return `[Circular ${type}${keyInfo}${moreKeys}]`; }; _BaseError_static = new WeakSet(); hasNativeCauseSupport_fn = function() { if (typeof process !== "undefined" && process.versions?.node) { const [major, minor] = process.versions.node.split(".").map(Number); return major > 16 || major === 16 && minor >= 9; } return typeof window !== "undefined" && "cause" in Error.prototype; }; /** * Captures and filters the stack trace without affecting global state. * Filters out internal BaseError frames for cleaner stack traces. */ captureStack_fn = function() { if (typeof Error.captureStackTrace === "function") { Error.captureStackTrace(this, this.constructor); return __privateMethod(this, _BaseError_instances, filterInternalFrames_fn).call(this, this.stack); } let tempStack; try { throw new Error(); } catch (e) { tempStack = e.stack; } if (!tempStack) { return void 0; } return __privateMethod(this, _BaseError_instances, filterInternalFrames_fn).call(this, tempStack); }; /** * Filters out internal BaseError frames and updates the error header. * This provides cleaner stack traces by removing implementation details. */ filterInternalFrames_fn = function(stack) { if (!stack) { return void 0; } const lines = stack.split("\n"); const filteredLines = []; filteredLines.push(`${this.name}: ${this.message}`); for (let i = 1; i < lines.length; i++) { const line = lines[i]; if (line.includes("#captureStack") || line.includes("#filterInternalFrames") || line.includes("BaseError.constructor") || line.includes("new BaseError") || line.includes("captureStack_fn") || // Compiled private method name line.includes("filterInternalFrames_fn") || // Compiled private method name // Skip the temporary error creation frame line.includes("Object.<anonymous>") && line.includes("captureStack")) { continue; } filteredLines.push(line); } return filteredLines.join("\n"); }; __privateAdd(_BaseError, _BaseError_static); var BaseError = _BaseError; // src/utils/guard.ts function guard(condition, error) { if (!condition) { throw error; } } export { BaseError, guard }; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map