From ed81cfbcc51e0de93d7702cd5a9fefde7f5c05bf Mon Sep 17 00:00:00 2001 From: bombillazo Date: Sun, 25 Jan 2026 12:49:53 -0400 Subject: [PATCH 1/5] Add CHANGELOG, LLMS documentation, and enhance type definitions - Introduced .gitattributes for custom merge strategy on JSONL files. - Created CHANGELOG.md to document project changes and versioning. - Added LLMS.md for comprehensive usage examples and core concepts of the error-x library. - Enhanced type definitions and documentation in index.ts and resolver.ts for better clarity and usability. --- .gitattributes | 3 + .gitignore | 3 + CHANGELOG.md | 223 ++++++++++++++++++++++++++++ LLMS.md | 337 +++++++++++++++++++++++++++++++++++++++++++ src/presets/index.ts | 67 +++++++++ src/resolver.ts | 101 +++++++++++-- src/types/index.ts | 44 +++++- 7 files changed, 766 insertions(+), 12 deletions(-) create mode 100644 .gitattributes create mode 100644 CHANGELOG.md create mode 100644 LLMS.md diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..807d598 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ + +# Use bd merge for beads JSONL files +.beads/issues.jsonl merge=beads diff --git a/.gitignore b/.gitignore index 4fda788..3022da5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ node_modules .git .claude +.beads +.ralph-tui +AGENTS.md dist diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1fcd30f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,223 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.6.0] - 2026-01-20 + +### Added + +- `ErrorXResolver` class for resolving ErrorX instances to enhanced presentation objects + - Type-safe error type determination via `onResolveType` callback + - i18n integration with custom resolver functions + - Documentation URL building with `docs.baseUrl` and `docsPath` options + - Custom config properties support for domain-specific needs + - Config hierarchy: defaults → type config → preset config + - Custom result type transformation via `onTransformResult` +- New type definitions in `types/resolver.types.ts` for resolver functionality + +### Changed + +- Refactored UI message exports for better organization + - `httpErrorUiMessages` now keyed by status code + - `dbErrorUiMessages` keyed by preset name + - `validationErrorUiMessage` for default validation message +- Split type definitions into separate files for better maintainability: + - `types/core.types.ts` - Core type definitions + - `types/resolver.types.ts` - Resolver-specific types + - `types/serialization.types.ts` - Serialization types + - `types/transform.types.ts` - Transform types + +### Removed + +- `uiMessage` property from ErrorX class (use `ErrorXResolver` instead) + +## [0.5.1] - 2026-01-19 + +### Changed + +- Enhanced serialization and deserialization to preserve all properties in error chains +- Improved chain restoration from JSON with full property preservation +- Renamed `ErrorXCause` type to `ErrorXSnapshot` for clarity + +## [0.5.0] - 2026-01-19 + +### Added + +- `DBErrorX` class with presets for common database errors: + - `CONNECTION_FAILED`, `QUERY_FAILED`, `UNIQUE_VIOLATION`, `FOREIGN_KEY_VIOLATION` + - `NOT_NULL_VIOLATION`, `CHECK_VIOLATION`, `TIMEOUT`, `DEADLOCK` + - `TRANSACTION_FAILED`, `SERIALIZATION_FAILURE` +- `ValidationErrorX` class with: + - `fromZodError()` static method for Zod integration + - `forField()` static method for field-specific validation errors + - Presets: `INVALID_FORMAT`, `REQUIRED_FIELD`, `OUT_OF_RANGE`, `INVALID_TYPE` + +### Changed + +- Major internal refactoring for improved code organization +- Updated format script configuration + +## [0.4.6] - 2025-10-06 + +### Added + +- Documentation section in README +- GitHub Pages configuration for API documentation + +### Changed + +- Improved documentation generation scripts + +## [0.4.5] - 2025-10-06 + +### Changed + +- Enhanced ErrorX metadata extraction from source errors +- Updated documentation with improved examples + +### Fixed + +- Homepage URL in package.json to reflect correct repository + +## [0.4.4] - 2025-10-06 + +### Changed + +- `timestamp` property now uses Unix epoch (milliseconds) instead of ISO string +- `withMetadata()` signature updated to support additional metadata type parameter +- Improved `toString()` output format for better readability + +## [0.4.3] - 2025-10-06 + +### Changed + +- HTTP presets now indexed by status code (e.g., `HTTPErrorX.create(404)`) +- Improved consistency in preset usage patterns + +## [0.4.2] - 2025-10-06 + +### Added + +- `cleanStackDelimiter` configuration option for trimming stack traces at a specific line + +### Changed + +- Renamed stack cleaning configuration for clarity + +## [0.4.1] - 2025-10-05 + +### Changed + +- Updated ErrorX cause type to `ErrorXCause` for improved type safety +- Refactored error handling to use `ErrorXCause` format for cause property + +## [0.4.0] - 2025-10-05 + +### Added + +- `HTTPErrorX` class with presets for all standard HTTP status codes (400-511) +- Type-safe metadata support with generics +- `ErrorX.configure()` static method for global configuration +- `cleanStack` configuration option with customizable patterns +- Frozen HTTP presets for immutability + +### Changed + +- `code` field in `ErrorXOptions` now accepts both string and number types +- Improved HTTP error preset messages for consistency + +### Fixed + +- Message casing for HTTP error presets + +## [0.3.0] - 2025-09-30 + +### Added + +- Preset support integrated directly into ErrorX class +- `ErrorX.create()` factory method for preset-based creation + +### Changed + +- Refactored actions structure for consistency +- Removed separate presets module in favor of integrated approach + +## [0.2.2] - 2025-09-30 + +### Added + +- Enhanced README with error presets documentation +- API documentation improvements with usage patterns + +## [0.2.1] - 2025-09-30 + +### Added + +- `httpStatus` field for HTTP error status codes +- `type` field for error categorization +- Presets for common HTTP error configurations + +### Fixed + +- Metadata access using optional chaining for safety +- Validation for `httpStatus` and `type` in constructor + +## [0.2.0] - 2025-09-29 + +### Added + +- `ERROR_X_OPTION_FIELDS` constant for centralized validation +- Smart conversion in constructor (supports string messages directly) + +### Changed + +- Enhanced `ErrorXOptions` type for improved flexibility and ergonomics +- Streamlined options conversion logic in constructor +- Updated documentation links to use scoped package name + +## [0.1.1] - 2025-08-18 + +### Added + +- Initial release of error-x +- `ErrorX` class extending native `Error` with: + - `code` property with automatic generation from name (UPPER_SNAKE_CASE) + - `message` for technical error descriptions + - `name` for error type identification + - `metadata` for additional context + - `timestamp` for error occurrence tracking + - `chain` property for full error sequence access + - `parent` and `root` properties for error chain navigation + - `original` property for non-ErrorX wrapped entities +- `ErrorX.from()` static method for converting any value to ErrorX +- `ErrorX.fromJSON()` for deserialization +- `ErrorX.isErrorX()` type guard +- `ErrorX.cleanStack()` for removing internal frames from stack traces +- `withMetadata()` instance method for creating errors with merged metadata +- `toJSON()` for serialization +- `toString()` for detailed string representation +- Safe JSON serialization handling circular references +- Comprehensive test suite +- Initial documentation + +[Unreleased]: https://github.com/bombillazo/error-x/compare/v0.6.0...HEAD +[0.6.0]: https://github.com/bombillazo/error-x/compare/v0.5.1...v0.6.0 +[0.5.1]: https://github.com/bombillazo/error-x/compare/v0.5.0...v0.5.1 +[0.5.0]: https://github.com/bombillazo/error-x/compare/v0.4.6...v0.5.0 +[0.4.6]: https://github.com/bombillazo/error-x/compare/v0.4.5...v0.4.6 +[0.4.5]: https://github.com/bombillazo/error-x/compare/v0.4.4...v0.4.5 +[0.4.4]: https://github.com/bombillazo/error-x/compare/v0.4.3...v0.4.4 +[0.4.3]: https://github.com/bombillazo/error-x/compare/v0.4.2...v0.4.3 +[0.4.2]: https://github.com/bombillazo/error-x/compare/v0.4.1...v0.4.2 +[0.4.1]: https://github.com/bombillazo/error-x/compare/v0.4.0...v0.4.1 +[0.4.0]: https://github.com/bombillazo/error-x/compare/v0.3.0...v0.4.0 +[0.3.0]: https://github.com/bombillazo/error-x/compare/v0.2.2...v0.3.0 +[0.2.2]: https://github.com/bombillazo/error-x/compare/v0.2.1...v0.2.2 +[0.2.1]: https://github.com/bombillazo/error-x/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/bombillazo/error-x/compare/v0.1.1...v0.2.0 +[0.1.1]: https://github.com/bombillazo/error-x/releases/tag/v0.1.1 diff --git a/LLMS.md b/LLMS.md new file mode 100644 index 0000000..f76cc9e --- /dev/null +++ b/LLMS.md @@ -0,0 +1,337 @@ +# error-x + +> TypeScript error handling library with type-safe metadata, error chaining, serialization, and preset-based error classes. + +## Quick Start + +```typescript +import { ErrorX, HTTPErrorX, DBErrorX, ValidationErrorX } from 'error-x'; + +// Basic usage +throw new ErrorX({ message: 'Operation failed', code: 'OP_FAILED' }); + +// HTTP errors with status code presets +throw HTTPErrorX.create(404, { message: 'User not found' }); + +// Database errors with presets +throw DBErrorX.create('CONNECTION_TIMEOUT', { metadata: { host: 'db.example.com' } }); + +// Validation errors from Zod +throw ValidationErrorX.fromZodError(zodError); + +// Error chaining (preserves cause chain) +throw new ErrorX({ message: 'High-level error', cause: originalError }); + +// Type-safe metadata +const error = new ErrorX<{ userId: number }>({ + message: 'User error', + metadata: { userId: 123 } +}); +console.log(error.metadata?.userId); // TypeScript knows this is number +``` + +## Core Concepts + +### ErrorX Class + +The base error class extending native `Error` with enhanced capabilities: + +| Property | Type | Description | +|----------|------|-------------| +| `code` | `string` | Error identifier (auto-generated from name as UPPER_SNAKE_CASE if not provided) | +| `metadata` | `TMetadata \| undefined` | Type-safe additional context | +| `timestamp` | `number` | Unix epoch when created | +| `httpStatus` | `number \| undefined` | Associated HTTP status code | +| `original` | `ErrorXSnapshot \| undefined` | Snapshot of wrapped non-ErrorX source (from `ErrorX.from()`) | +| `parent` | `ErrorX \| undefined` | Immediate parent in error chain | +| `root` | `ErrorX \| undefined` | Deepest cause in chain | +| `chain` | `readonly ErrorX[]` | Full chain `[this, parent, grandparent, ...]` | + +### Constructor Signatures + +```typescript +new ErrorX() // Default message "An error occurred" +new ErrorX('Message string') // String message +new ErrorX({ message, name, code, cause, metadata, httpStatus }) // Full options +new ErrorX({ metadata: {...} }) // Type-safe metadata +``` + +### Static Methods + +| Method | Description | +|--------|-------------| +| `ErrorX.from(value, overrides?)` | Wraps any value into ErrorX. Stores original in `.original` property. | +| `ErrorX.fromJSON(serialized)` | Reconstructs ErrorX from serialized form | +| `ErrorX.isErrorX(value)` | Type guard: `value is ErrorX` | +| `ErrorX.isErrorXOptions(value)` | Validates if object is valid ErrorXOptions | +| `ErrorX.configure(config)` | Set global config (cleanStack, cleanStackDelimiter) | +| `ErrorX.getConfig()` | Get current global config | +| `ErrorX.resetConfig()` | Reset global config to null | +| `ErrorX.cleanStack(stack?, delimiter?)` | Clean internal frames from stack trace | + +### Instance Methods + +| Method | Description | +|--------|-------------| +| `.withMetadata(additional)` | Returns new ErrorX with merged metadata | +| `.toJSON()` | Serializes to JSON-compatible object | +| `.toString()` | Detailed string representation | + +## Preset Error Classes + +### HTTPErrorX + +HTTP errors with status code presets (400-511): + +```typescript +HTTPErrorX.create(404) // NotFoundError with httpStatus: 404 +HTTPErrorX.create(401, { message: 'Invalid token' }) +HTTPErrorX.create({ message: 'Error' }) // Uses default 500 + +// Available presets: 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, +// 411, 412, 413, 414, 415, 416, 417, 418, 422, 423, 424, 425, 426, 428, 429, +// 431, 451, 500, 501, 502, 503, 504, 505, 506, 507, 508, 510, 511 +``` + +### DBErrorX + +Database errors with string presets: + +```typescript +DBErrorX.create('CONNECTION_FAILED') +DBErrorX.create('QUERY_TIMEOUT', { metadata: { query: 'SELECT...' } }) +DBErrorX.create({ message: 'Custom DB error' }) // Uses default UNKNOWN + +// Available presets: CONNECTION_FAILED, CONNECTION_TIMEOUT, CONNECTION_REFUSED, +// CONNECTION_LOST, QUERY_FAILED, QUERY_TIMEOUT, SYNTAX_ERROR, UNIQUE_VIOLATION, +// FOREIGN_KEY_VIOLATION, NOT_NULL_VIOLATION, CHECK_VIOLATION, TRANSACTION_FAILED, +// DEADLOCK, NOT_FOUND, UNKNOWN +``` + +### ValidationErrorX + +Validation errors with Zod integration: + +```typescript +// From Zod error +ValidationErrorX.fromZodError(zodError) +ValidationErrorX.fromZodError(zodError, { httpStatus: 422 }) + +// For specific field +ValidationErrorX.forField('email', 'Invalid format') +ValidationErrorX.forField('age', 'Must be 18+', { code: 'TOO_YOUNG' }) + +// Direct creation +ValidationErrorX.create({ message: 'Validation failed', metadata: { field: 'email' } }) +``` + +## Error Chaining + +```typescript +// Chain via cause option +const dbError = new ErrorX({ message: 'DB query failed' }); +const serviceError = new ErrorX({ message: 'User fetch failed', cause: dbError }); +const apiError = new ErrorX({ message: 'API request failed', cause: serviceError }); + +// Navigate chain +apiError.parent; // serviceError +apiError.root; // dbError +apiError.chain; // [apiError, serviceError, dbError] +apiError.chain.length; // 3 + +// Auto-wrapping: non-ErrorX causes are automatically wrapped +new ErrorX({ cause: new Error('native') }); // Works, wraps into ErrorX +``` + +## Serialization + +```typescript +// Serialize +const json = error.toJSON(); +// Returns ErrorXSerialized: { name, message, code, metadata, timestamp, stack?, httpStatus?, original?, chain? } + +// Deserialize +const restored = ErrorX.fromJSON(json); + +// Works with JSON.stringify +JSON.stringify(error); // Uses toJSON() automatically +``` + +## ErrorXResolver + +Resolves errors to user-friendly presentations with i18n support: + +```typescript +import { ErrorXResolver } from 'error-x'; + +const resolver = new ErrorXResolver({ + i18n: { + resolver: (key, params) => i18next.t(key, params), + keyTemplate: '{namespace}.{code}', // Default + }, + docs: { baseUrl: 'https://docs.example.com' }, + onResolveType: (error) => { + if (error instanceof HTTPErrorX) return 'http'; + if (error instanceof DBErrorX) return 'database'; + return 'general'; + }, + defaults: { namespace: 'errors', uiMessage: 'An error occurred' }, + configs: { + http: { namespace: 'errors.http', docsPath: '/http' }, + database: { namespace: 'errors.db', docsPath: '/db' }, + general: { namespace: 'errors' }, + }, +}); + +const result = resolver.resolve(error, 'en'); +// { uiMessage, docsUrl, i18nKey, errorType, config } +``` + +## Creating Custom Error Classes + +```typescript +type PaymentMetadata = { transactionId?: string; amount?: number }; + +class PaymentErrorX extends ErrorX { + static presets = { + DECLINED: { message: 'Payment declined', code: 'DECLINED', httpStatus: 402 }, + EXPIRED: { message: 'Card expired', code: 'EXPIRED', httpStatus: 400 }, + INSUFFICIENT: { message: 'Insufficient funds', code: 'INSUFFICIENT', httpStatus: 402 }, + }; + static defaultPreset = 'DECLINED'; + static defaults = { name: 'PaymentError', httpStatus: 400 }; + static transform: ErrorXTransform = (opts) => ({ + ...opts, + code: `PAYMENT_${opts.code}`, + }); + + static override create( + preset?: keyof typeof PaymentErrorX.presets, + overrides?: Partial> + ): PaymentErrorX { + return ErrorX.create.call(PaymentErrorX, preset, overrides) as PaymentErrorX; + } +} + +PaymentErrorX.create('DECLINED', { metadata: { transactionId: 'tx_123' } }); +``` + +## Type Exports + +```typescript +// Core types +import type { + ErrorXOptions, // Constructor options + ErrorXMetadata, // Record + ErrorXSerialized, // Serialized form + ErrorXSnapshot, // Original error snapshot + ErrorXConfig, // Global configuration + ErrorXOptionField, // Valid option field names +} from 'error-x'; + +// Transform types +import type { + ErrorXTransform, // Transform function signature + ErrorXTransformContext, // Context passed to transform +} from 'error-x'; + +// Resolver types +import type { + ErrorXResolverOptions, // Full resolver options + ErrorXResolverConfig, // Type helper for custom config + ErrorXBaseConfig, // Base resolver config + ErrorXResolverTypeConfig, // Per-type config + ErrorXResolverI18nConfig, // i18n configuration + ErrorXResolverDocsConfig, // Docs configuration + ResolveContext, // Context passed to onResolve +} from 'error-x'; + +// Preset types +import type { + HTTPErrorXMetadata, // { endpoint?, method?, [key]: unknown } + HTTPStatusCode, // Union of status code numbers + DBErrorXMetadata, // { query?, table?, database?, operation?, ... } + DBErrorPreset, // Union of DB preset strings + ValidationErrorXMetadata, // { field?, path?, zodCode?, expected?, ... } + ZodIssue, // Zod issue structure +} from 'error-x'; +``` + +## UI Message Objects + +Pre-defined user-friendly messages for presets: + +```typescript +import { httpErrorUiMessages, dbErrorUiMessages, validationErrorUiMessage } from 'error-x'; + +httpErrorUiMessages[404]; // "The requested resource could not be found." +dbErrorUiMessages['UNIQUE_VIOLATION']; // "This record already exists." +validationErrorUiMessage; // "The provided input is invalid. Please check your data." +``` + +## Global Configuration + +```typescript +ErrorX.configure({ + cleanStack: true, // Enable stack cleaning (default) + cleanStack: false, // Disable stack cleaning + cleanStack: ['pattern1', 'pattern2'], // Custom patterns to remove + cleanStackDelimiter: 'app-entry', // Trim stack after this line +}); + +ErrorX.getConfig(); // Get current config +ErrorX.resetConfig(); // Reset to null +``` + +## Common Patterns + +### Catch and Wrap + +```typescript +try { + await riskyOperation(); +} catch (err) { + throw new ErrorX({ message: 'Operation failed', cause: err }); +} +``` + +### Type Narrowing + +```typescript +try { + await fetchData(); +} catch (err) { + if (ErrorX.isErrorX(err)) { + console.log(err.code, err.metadata); + } + if (err instanceof HTTPErrorX) { + console.log(err.httpStatus); + } + if (err instanceof ValidationErrorX) { + console.log(err.metadata?.field, err.metadata?.issues); + } +} +``` + +### Enriching Errors + +```typescript +const error = new ErrorX({ message: 'API failed', metadata: { endpoint: '/api' } }); +const enriched = error.withMetadata({ userId: 123, retryCount: 3 }); +// metadata: { endpoint: '/api', userId: 123, retryCount: 3 } +``` + +### Error Conversion + +```typescript +// ErrorX.from() extracts properties from various error formats: +// - Standard Error: extracts name, message, stack, cause +// - API objects: extracts message/details/text/info/error/errorMessage, +// name/title, code, status/statusCode/httpStatus, metadata +// - Strings: uses as message +// - Unknown: converts to string message + +const apiResponse = { error: 'Not found', code: 404, status: 404 }; +const error = ErrorX.from(apiResponse); +// message: 'Not found', code: '404', httpStatus: 404 +``` diff --git a/src/presets/index.ts b/src/presets/index.ts index 22fd26f..e5ac267 100644 --- a/src/presets/index.ts +++ b/src/presets/index.ts @@ -1,15 +1,82 @@ +/** + * Pre-built error classes for common use cases. + * + * @remarks + * This module exports specialized ErrorX subclasses with preset configurations: + * + * **HTTPErrorX** - HTTP errors with status code presets (400-511) + * - Use `HTTPErrorX.create(404)` for quick error creation + * - Automatic `httpStatus` from preset key + * - Includes user-friendly messages via `httpErrorUiMessages` + * + * **DBErrorX** - Database errors with common scenario presets + * - Use `DBErrorX.create('CONNECTION_FAILED')` for preset creation + * - Automatic `DB_` code prefix via transform + * - Includes user-friendly messages via `dbErrorUiMessages` + * + * **ValidationErrorX** - Validation errors with Zod integration + * - Use `ValidationErrorX.fromZodError(zodError)` for Zod errors + * - Use `ValidationErrorX.forField(field, message)` for manual errors + * - Automatic `VALIDATION_` code prefix via transform + * + * @example + * ```typescript + * import { HTTPErrorX, DBErrorX, ValidationErrorX } from 'error-x'; + * + * // HTTP errors + * throw HTTPErrorX.create(404, { message: 'User not found' }); + * + * // Database errors + * throw DBErrorX.create('QUERY_TIMEOUT', { metadata: { query: sql } }); + * + * // Validation errors + * throw ValidationErrorX.fromZodError(zodError); + * ``` + * + * @packageDocumentation + */ + +/** + * Database error exports. + * + * @remarks + * - {@link DBErrorX} - Database error class with presets + * - {@link DBErrorXMetadata} - Typed metadata for DB context (query, table, operation, etc.) + * - {@link DBErrorPreset} - Union type of valid preset keys + * - {@link dbErrorUiMessages} - User-friendly messages keyed by preset + */ export { DBErrorX, type DBErrorXMetadata, type DBErrorXPresetKey as DBErrorPreset, dbErrorUiMessages, } from './db-error'; + +/** + * HTTP error exports. + * + * @remarks + * - {@link HTTPErrorX} - HTTP error class with status code presets + * - {@link HTTPErrorXMetadata} - Typed metadata for HTTP context (endpoint, method, etc.) + * - {@link HTTPStatusCode} - Union type of valid status codes + * - {@link httpErrorUiMessages} - User-friendly messages keyed by status code + */ export { HTTPErrorX, type HTTPErrorXMetadata, type HTTPErrorXPresetKey as HTTPStatusCode, httpErrorUiMessages, } from './http-error'; + +/** + * Validation error exports. + * + * @remarks + * - {@link ValidationErrorX} - Validation error class with Zod integration + * - {@link ValidationErrorXMetadata} - Typed metadata for validation context + * - {@link validationErrorUiMessage} - Default user-friendly message + * - {@link ZodIssue} - Type-compatible Zod issue structure + */ export { ValidationErrorX, type ValidationErrorXMetadata, diff --git a/src/resolver.ts b/src/resolver.ts index 6f4a8ee..7b74b29 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -69,7 +69,27 @@ export class ErrorXResolver< } /** - * Merges config from defaults → type → preset + * Merges configuration from multiple layers in priority order. + * + * Configuration is merged in this order (later values override earlier): + * 1. `defaults` - Base configuration for all error types + * 2. `configs[errorType]` - Type-specific configuration + * 3. `configs[errorType].presets[error.code]` - Per-error-code overrides + * + * @param error - The ErrorX instance being resolved + * @param errorType - The error type key returned by `onResolveType` + * @returns Merged configuration object + * + * @example + * ```typescript + * // Given this resolver setup: + * // defaults: { namespace: 'errors' } + * // configs.api: { namespace: 'errors.api', docsPath: '/api' } + * // configs.api.presets.AUTH_EXPIRED: { docsPath: '/api/auth' } + * + * // For an error with code 'AUTH_EXPIRED' and type 'api': + * // Result: { namespace: 'errors.api', docsPath: '/api/auth' } + * ``` */ private mergeConfig(error: ErrorX, errorType: string): TConfig { const typeConfig = this.options.configs[errorType] as @@ -90,7 +110,29 @@ export class ErrorXResolver< } /** - * Builds the i18n key from template + * Builds the i18n translation key from the configured template. + * + * Supports these placeholders in the template: + * - `{namespace}` - Replaced with `config.namespace` + * - `{code}` - Replaced with `error.code` + * - `{name}` - Replaced with `error.name` + * - `{errorType}` - Replaced with the resolved error type + * + * @param error - The ErrorX instance being resolved + * @param config - The merged configuration for this error + * @param errorType - The error type key returned by `onResolveType` + * @returns The built translation key string + * + * @example + * ```typescript + * // With template '{namespace}.{code}' (default): + * // namespace: 'errors.api', code: 'AUTH_EXPIRED' + * // Result: 'errors.api.AUTH_EXPIRED' + * + * // With template '{errorType}.{name}.{code}': + * // errorType: 'api', name: 'AuthError', code: 'AUTH_EXPIRED' + * // Result: 'api.AuthError.AUTH_EXPIRED' + * ``` */ private buildI18nKey(error: ErrorX, config: TConfig, errorType: string): string { const template = this.options.i18n?.keyTemplate ?? this.defaultKeyTemplate; @@ -104,12 +146,34 @@ export class ErrorXResolver< } /** - * Resolves uiMessage following the priority order: - * 1. presets[code].uiMessage (already merged into config if from preset) - * 2. i18n resolver result (if configured) - * 3. configs[errorType].uiMessage (fallback) - * 4. defaults.uiMessage (fallback) - * 5. undefined + * Resolves the user-friendly message for an error. + * + * Resolution follows this priority order (first defined value wins): + * 1. `configs[errorType].presets[code].uiMessage` - Most specific override + * 2. `i18n.resolver(key, params)` - Translation from i18n library (if configured) + * 3. `configs[errorType].uiMessage` - Type-level fallback message + * 4. `defaults.uiMessage` - Global fallback message + * 5. `undefined` - No message available + * + * When using i18n, the resolver function receives the built i18n key and + * the error's metadata as interpolation parameters. + * + * @param error - The ErrorX instance being resolved + * @param _config - The merged configuration (unused, config accessed via options) + * @param i18nKey - The pre-built translation key + * @param errorType - The error type key returned by `onResolveType` + * @returns The resolved user-friendly message, or undefined + * + * @example + * ```typescript + * // Priority 1: Preset-specific message + * // configs.api.presets.AUTH_EXPIRED.uiMessage: 'Your session has expired' + * // Result: 'Your session has expired' + * + * // Priority 2: i18n translation (if no preset message) + * // i18n.resolver('errors.api.AUTH_EXPIRED', { userId: 123 }) + * // Result: Translation from i18n library + * ``` */ private resolveUiMessage( error: ErrorX, @@ -151,7 +215,26 @@ export class ErrorXResolver< } /** - * Builds the full documentation URL + * Builds the full documentation URL for an error. + * + * Constructs the URL by combining: + * - `docs.baseUrl` - The base documentation URL (e.g., 'https://docs.example.com') + * - `config.docsPath` - The path segment for this error type (e.g., '/api') + * - `#error.code` - Hash fragment for the specific error code + * + * Returns an empty string if both baseUrl and docsPath are empty. + * + * @param error - The ErrorX instance being resolved + * @param config - The merged configuration for this error + * @returns The full documentation URL, or empty string if not configured + * + * @example + * ```typescript + * // docs.baseUrl: 'https://docs.example.com' + * // config.docsPath: '/api/errors' + * // error.code: 'AUTH_EXPIRED' + * // Result: 'https://docs.example.com/api/errors#AUTH_EXPIRED' + * ``` */ private buildDocsUrl(error: ErrorX, config: TConfig): string { const baseUrl = this.options.docs?.baseUrl ?? ''; diff --git a/src/types/index.ts b/src/types/index.ts index 44c4286..557d47b 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1,17 @@ +/** + * Core type definitions for ErrorX. + * + * @remarks + * These types define the fundamental structures for creating and configuring ErrorX instances: + * - {@link ErrorXOptions} - Constructor options for creating errors + * - {@link ErrorXMetadata} - Type-safe metadata storage + * - {@link ErrorXSnapshot} - Serialized representation of original errors + * - {@link ErrorXOptionField} - Valid option field names + * - {@link ErrorXBasePresetKey} - Base type for preset keys + * + * @packageDocumentation + */ + // Core types export { ERROR_X_OPTION_FIELDS, @@ -7,7 +21,17 @@ export { type ErrorXOptions, type ErrorXSnapshot, } from './core.types'; -// Resolver types + +/** + * Resolver type definitions for ErrorXResolver. + * + * @remarks + * These types support the ErrorXResolver class for error presentation: + * - {@link ErrorXResolverOptions} - Full configuration for resolver instances + * - {@link ErrorXResolverConfig} - Type helper for custom config extensions + * - {@link ErrorXBaseConfig} - Base configuration properties + * - {@link ResolveContext} - Context object returned by resolve() + */ export type { ErrorXBaseConfig, ErrorXResolverConfig, @@ -18,7 +42,21 @@ export type { ResolveContext, } from './resolver.types'; -// Serialization types +/** + * Serialization type definitions. + * + * @remarks + * {@link ErrorXSerialized} - JSON-serializable representation for network transmission + * and storage. Used by `toJSON()` and `fromJSON()` methods. + */ export type { ErrorXSerialized } from './serialization.types'; -// Transform types + +/** + * Transform type definitions for custom error classes. + * + * @remarks + * These types support the `.create()` factory method's transform feature: + * - {@link ErrorXTransform} - Function signature for option transformation + * - {@link ErrorXTransformContext} - Context passed to transform functions + */ export type { ErrorXTransform, ErrorXTransformContext } from './transform.types'; From d2fec211354e31f38d3c73e7a6533a5194841d33 Mon Sep 17 00:00:00 2001 From: bombillazo Date: Sun, 25 Jan 2026 12:56:38 -0400 Subject: [PATCH 2/5] Bump version to 0.6.1 in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0c79922..0d7efde 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@bombillazo/error-x", "description": "A simple and consistent error handling library for TypeScript applications. Provides type-safe error handling with great DX, solving common pain points like unknown error types, lost stack traces, async error handling, and error serialization. Isomorphic and framework-agnostic.", - "version": "0.6.0", + "version": "0.6.1", "author": "Hector Ayala ", "dependencies": { "deepmerge-ts": "^7.1.5", From 380fe0f31001cfe460abbd693cb40bd3a1bffdf4 Mon Sep 17 00:00:00 2001 From: Hector Ayala Date: Sun, 25 Jan 2026 13:14:10 -0400 Subject: [PATCH 3/5] Update LLMS.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- LLMS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LLMS.md b/LLMS.md index f76cc9e..439b250 100644 --- a/LLMS.md +++ b/LLMS.md @@ -5,7 +5,7 @@ ## Quick Start ```typescript -import { ErrorX, HTTPErrorX, DBErrorX, ValidationErrorX } from 'error-x'; +import { ErrorX, HTTPErrorX, DBErrorX, ValidationErrorX } from '@bombillazo/error-x'; // Basic usage throw new ErrorX({ message: 'Operation failed', code: 'OP_FAILED' }); From 42f72551d9796db1902d7148cdc98f7501e6d3f2 Mon Sep 17 00:00:00 2001 From: Hector Ayala Date: Sun, 25 Jan 2026 13:14:37 -0400 Subject: [PATCH 4/5] Update src/presets/index.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/presets/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/presets/index.ts b/src/presets/index.ts index e5ac267..f21a18f 100644 --- a/src/presets/index.ts +++ b/src/presets/index.ts @@ -21,7 +21,7 @@ * * @example * ```typescript - * import { HTTPErrorX, DBErrorX, ValidationErrorX } from 'error-x'; + * import { HTTPErrorX, DBErrorX, ValidationErrorX } from '@bombillazo/error-x'; * * // HTTP errors * throw HTTPErrorX.create(404, { message: 'User not found' }); From 4044ed8220e7d799edce777b717beeb6c0cc5af6 Mon Sep 17 00:00:00 2001 From: bombillazo Date: Sun, 25 Jan 2026 13:17:57 -0400 Subject: [PATCH 5/5] Update LLMS.md to use scoped package imports and enhance configuration examples --- LLMS.md | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/LLMS.md b/LLMS.md index 439b250..1823057 100644 --- a/LLMS.md +++ b/LLMS.md @@ -162,7 +162,7 @@ JSON.stringify(error); // Uses toJSON() automatically Resolves errors to user-friendly presentations with i18n support: ```typescript -import { ErrorXResolver } from 'error-x'; +import { ErrorXResolver } from '@bombillazo/error-x'; const resolver = new ErrorXResolver({ i18n: { @@ -227,13 +227,13 @@ import type { ErrorXSnapshot, // Original error snapshot ErrorXConfig, // Global configuration ErrorXOptionField, // Valid option field names -} from 'error-x'; +} from '@bombillazo/error-x'; // Transform types import type { ErrorXTransform, // Transform function signature ErrorXTransformContext, // Context passed to transform -} from 'error-x'; +} from '@bombillazo/error-x'; // Resolver types import type { @@ -254,7 +254,7 @@ import type { DBErrorPreset, // Union of DB preset strings ValidationErrorXMetadata, // { field?, path?, zodCode?, expected?, ... } ZodIssue, // Zod issue structure -} from 'error-x'; +} from '@bombillazo/error-x'; ``` ## UI Message Objects @@ -262,7 +262,7 @@ import type { Pre-defined user-friendly messages for presets: ```typescript -import { httpErrorUiMessages, dbErrorUiMessages, validationErrorUiMessage } from 'error-x'; +import { httpErrorUiMessages, dbErrorUiMessages, validationErrorUiMessage } from '@bombillazo/error-x'; httpErrorUiMessages[404]; // "The requested resource could not be found." dbErrorUiMessages['UNIQUE_VIOLATION']; // "This record already exists." @@ -272,12 +272,21 @@ validationErrorUiMessage; // "The provided input is invalid. Please check your ## Global Configuration ```typescript -ErrorX.configure({ - cleanStack: true, // Enable stack cleaning (default) - cleanStack: false, // Disable stack cleaning - cleanStack: ['pattern1', 'pattern2'], // Custom patterns to remove - cleanStackDelimiter: 'app-entry', // Trim stack after this line -}); +// Enable stack cleaning (default) and trim stack after a delimiter +ErrorX.configure({ + cleanStack: true, + cleanStackDelimiter: 'app-entry', // Trim stack after this line +}); + +// Disable stack cleaning +ErrorX.configure({ + cleanStack: false, +}); + +// Use custom patterns to remove from the stack +ErrorX.configure({ + cleanStack: ['pattern1', 'pattern2'], +}); ErrorX.getConfig(); // Get current config ErrorX.resetConfig(); // Reset to null