11/**
22 * Error handling utilities for dtsx
3+ * Provides custom error classes and formatting utilities
34 */
45
56import type { DtsError , SourceLocation } from './types'
@@ -20,6 +21,8 @@ export const ErrorCodes = {
2021 // Type errors
2122 TYPE_INFERENCE_ERROR : 'TYPE_INFERENCE_ERROR' ,
2223 UNRESOLVED_TYPE : 'UNRESOLVED_TYPE' ,
24+ EXTRACTION_ERROR : 'EXTRACTION_ERROR' ,
25+ PROCESSING_ERROR : 'PROCESSING_ERROR' ,
2326
2427 // Validation errors
2528 VALIDATION_ERROR : 'VALIDATION_ERROR' ,
@@ -29,12 +32,192 @@ export const ErrorCodes = {
2932 CONFIG_ERROR : 'CONFIG_ERROR' ,
3033 INVALID_ENTRYPOINT : 'INVALID_ENTRYPOINT' ,
3134
35+ // Dependency errors
36+ CIRCULAR_DEPENDENCY : 'CIRCULAR_DEPENDENCY' ,
37+
38+ // Operation errors
39+ TIMEOUT_ERROR : 'TIMEOUT_ERROR' ,
40+ NOT_SUPPORTED : 'NOT_SUPPORTED' ,
41+
3242 // Unknown
3343 UNKNOWN_ERROR : 'UNKNOWN_ERROR' ,
3444} as const
3545
3646export type ErrorCode = typeof ErrorCodes [ keyof typeof ErrorCodes ]
3747
48+ /**
49+ * Base error class for dtsx errors
50+ */
51+ export class DtsxError extends Error {
52+ /** Error code for programmatic handling */
53+ readonly code : ErrorCode
54+
55+ /** Additional context about the error */
56+ readonly context ?: Record < string , unknown >
57+
58+ constructor ( message : string , code : ErrorCode = 'UNKNOWN_ERROR' , context ?: Record < string , unknown > ) {
59+ super ( message )
60+ this . name = 'DtsxError'
61+ this . code = code
62+ this . context = context
63+
64+ // Maintains proper stack trace
65+ if ( Error . captureStackTrace ) {
66+ Error . captureStackTrace ( this , this . constructor )
67+ }
68+ }
69+
70+ /** Format error for logging */
71+ toString ( ) : string {
72+ let str = `${ this . name } [${ this . code } ]: ${ this . message } `
73+ if ( this . context ) {
74+ str += `\nContext: ${ JSON . stringify ( this . context , null , 2 ) } `
75+ }
76+ return str
77+ }
78+
79+ /** Convert to JSON for serialization */
80+ toJSON ( ) : Record < string , unknown > {
81+ return {
82+ name : this . name ,
83+ code : this . code ,
84+ message : this . message ,
85+ context : this . context ,
86+ stack : this . stack ,
87+ }
88+ }
89+ }
90+
91+ /**
92+ * Error during file parsing
93+ */
94+ export class ParseError extends DtsxError {
95+ readonly filePath : string
96+ readonly line ?: number
97+ readonly column ?: number
98+
99+ constructor ( message : string , filePath : string , options ?: { line ?: number , column ?: number , cause ?: Error } ) {
100+ super ( message , 'PARSE_ERROR' , { filePath, line : options ?. line , column : options ?. column } )
101+ this . name = 'ParseError'
102+ this . filePath = filePath
103+ this . line = options ?. line
104+ this . column = options ?. column
105+ if ( options ?. cause ) this . cause = options . cause
106+ }
107+
108+ get locationString ( ) : string {
109+ if ( this . line !== undefined && this . column !== undefined ) {
110+ return `${ this . filePath } :${ this . line } :${ this . column } `
111+ }
112+ return this . line !== undefined ? `${ this . filePath } :${ this . line } ` : this . filePath
113+ }
114+ }
115+
116+ /**
117+ * Error during declaration extraction
118+ */
119+ export class ExtractionError extends DtsxError {
120+ readonly filePath : string
121+ readonly declarationKind ?: string
122+
123+ constructor ( message : string , filePath : string , declarationKind ?: string , cause ?: Error ) {
124+ super ( message , 'EXTRACTION_ERROR' , { filePath, declarationKind } )
125+ this . name = 'ExtractionError'
126+ this . filePath = filePath
127+ this . declarationKind = declarationKind
128+ if ( cause ) this . cause = cause
129+ }
130+ }
131+
132+ /**
133+ * Error during type processing
134+ */
135+ export class ProcessingError extends DtsxError {
136+ readonly declarationName ?: string
137+
138+ constructor ( message : string , declarationName ?: string , cause ?: Error ) {
139+ super ( message , 'PROCESSING_ERROR' , { declarationName } )
140+ this . name = 'ProcessingError'
141+ this . declarationName = declarationName
142+ if ( cause ) this . cause = cause
143+ }
144+ }
145+
146+ /**
147+ * Error during file I/O operations
148+ */
149+ export class FileError extends DtsxError {
150+ readonly filePath : string
151+ readonly operation : 'read' | 'write' | 'delete' | 'stat' | 'glob'
152+
153+ constructor ( message : string , filePath : string , operation : 'read' | 'write' | 'delete' | 'stat' | 'glob' , cause ?: Error ) {
154+ super ( message , operation === 'read' ? 'FILE_READ_ERROR' : 'FILE_WRITE_ERROR' , { filePath, operation } )
155+ this . name = 'FileError'
156+ this . filePath = filePath
157+ this . operation = operation
158+ if ( cause ) this . cause = cause
159+ }
160+ }
161+
162+ /**
163+ * Error during configuration loading or validation
164+ */
165+ export class ConfigError extends DtsxError {
166+ readonly configPath ?: string
167+ readonly invalidKey ?: string
168+
169+ constructor ( message : string , options ?: { configPath ?: string , invalidKey ?: string , cause ?: Error } ) {
170+ super ( message , 'CONFIG_ERROR' , { configPath : options ?. configPath , invalidKey : options ?. invalidKey } )
171+ this . name = 'ConfigError'
172+ this . configPath = options ?. configPath
173+ this . invalidKey = options ?. invalidKey
174+ if ( options ?. cause ) this . cause = options . cause
175+ }
176+ }
177+
178+ /**
179+ * Error when circular dependency is detected
180+ */
181+ export class CircularDependencyError extends DtsxError {
182+ readonly cycle : string [ ]
183+
184+ constructor ( cycle : string [ ] ) {
185+ super ( `Circular dependency detected: ${ cycle . join ( ' -> ' ) } ` , 'CIRCULAR_DEPENDENCY' , { cycle } )
186+ this . name = 'CircularDependencyError'
187+ this . cycle = cycle
188+ }
189+ }
190+
191+ /**
192+ * Type guards for error types
193+ */
194+ export function isDtsxError ( error : unknown ) : error is DtsxError {
195+ return error instanceof DtsxError
196+ }
197+
198+ export function isParseError ( error : unknown ) : error is ParseError {
199+ return error instanceof ParseError
200+ }
201+
202+ export function isFileError ( error : unknown ) : error is FileError {
203+ return error instanceof FileError
204+ }
205+
206+ export function isConfigError ( error : unknown ) : error is ConfigError {
207+ return error instanceof ConfigError
208+ }
209+
210+ /**
211+ * Wrap an unknown error in a DtsxError
212+ */
213+ export function wrapError ( error : unknown , code : ErrorCode = 'UNKNOWN_ERROR' , message ?: string ) : DtsxError {
214+ if ( error instanceof DtsxError ) return error
215+ const errorMessage = message || ( error instanceof Error ? error . message : String ( error ) )
216+ const wrapped = new DtsxError ( errorMessage , code )
217+ if ( error instanceof Error ) wrapped . cause = error
218+ return wrapped
219+ }
220+
38221/**
39222 * Calculate line and column from source code offset
40223 */
0 commit comments