errors.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /**
  2. * CodeGraph Error Classes
  3. *
  4. * Custom error types for better error handling and debugging.
  5. *
  6. * @module errors
  7. *
  8. * @example
  9. * ```typescript
  10. * import { FileError, ParseError, setLogger, silentLogger } from 'codegraph';
  11. *
  12. * // Catch specific error types
  13. * try {
  14. * await cg.indexAll();
  15. * } catch (error) {
  16. * if (error instanceof FileError) {
  17. * console.log(`File error at ${error.filePath}: ${error.message}`);
  18. * } else if (error instanceof ParseError) {
  19. * console.log(`Parse error at ${error.filePath}:${error.line}`);
  20. * }
  21. * }
  22. *
  23. * // Disable logging for tests
  24. * setLogger(silentLogger);
  25. * ```
  26. */
  27. /**
  28. * Base error class for all CodeGraph errors.
  29. *
  30. * All CodeGraph-specific errors extend this class, allowing you to catch
  31. * all CodeGraph errors with a single catch block.
  32. *
  33. * @example
  34. * ```typescript
  35. * try {
  36. * await cg.indexAll();
  37. * } catch (error) {
  38. * if (error instanceof CodeGraphError) {
  39. * console.log(`CodeGraph error [${error.code}]: ${error.message}`);
  40. * }
  41. * }
  42. * ```
  43. */
  44. export class CodeGraphError extends Error {
  45. /** Error code for categorization (e.g., 'FILE_ERROR', 'PARSE_ERROR') */
  46. readonly code: string;
  47. /** Additional context about the error */
  48. readonly context?: Record<string, unknown>;
  49. constructor(message: string, code: string, context?: Record<string, unknown>) {
  50. super(message);
  51. this.name = 'CodeGraphError';
  52. this.code = code;
  53. this.context = context;
  54. // Maintain proper stack trace for V8
  55. if (Error.captureStackTrace) {
  56. Error.captureStackTrace(this, this.constructor);
  57. }
  58. }
  59. }
  60. /**
  61. * Error reading or accessing files
  62. */
  63. export class FileError extends CodeGraphError {
  64. readonly filePath: string;
  65. constructor(message: string, filePath: string, cause?: Error) {
  66. super(message, 'FILE_ERROR', { filePath, cause: cause?.message });
  67. this.name = 'FileError';
  68. this.filePath = filePath;
  69. if (cause) {
  70. this.cause = cause;
  71. }
  72. }
  73. }
  74. /**
  75. * Error parsing source code
  76. */
  77. export class ParseError extends CodeGraphError {
  78. readonly filePath: string;
  79. readonly line?: number;
  80. readonly column?: number;
  81. constructor(
  82. message: string,
  83. filePath: string,
  84. options?: { line?: number; column?: number; cause?: Error }
  85. ) {
  86. super(message, 'PARSE_ERROR', {
  87. filePath,
  88. line: options?.line,
  89. column: options?.column,
  90. cause: options?.cause?.message,
  91. });
  92. this.name = 'ParseError';
  93. this.filePath = filePath;
  94. this.line = options?.line;
  95. this.column = options?.column;
  96. if (options?.cause) {
  97. this.cause = options.cause;
  98. }
  99. }
  100. }
  101. /**
  102. * Error with database operations
  103. */
  104. export class DatabaseError extends CodeGraphError {
  105. readonly operation: string;
  106. constructor(message: string, operation: string, cause?: Error) {
  107. super(message, 'DATABASE_ERROR', { operation, cause: cause?.message });
  108. this.name = 'DatabaseError';
  109. this.operation = operation;
  110. if (cause) {
  111. this.cause = cause;
  112. }
  113. }
  114. }
  115. /**
  116. * Error with search operations
  117. */
  118. export class SearchError extends CodeGraphError {
  119. readonly query: string;
  120. constructor(message: string, query: string, cause?: Error) {
  121. super(message, 'SEARCH_ERROR', { query, cause: cause?.message });
  122. this.name = 'SearchError';
  123. this.query = query;
  124. if (cause) {
  125. this.cause = cause;
  126. }
  127. }
  128. }
  129. /**
  130. * Error with vector/embedding operations
  131. */
  132. export class VectorError extends CodeGraphError {
  133. constructor(message: string, operation: string, cause?: Error) {
  134. super(message, 'VECTOR_ERROR', { operation, cause: cause?.message });
  135. this.name = 'VectorError';
  136. if (cause) {
  137. this.cause = cause;
  138. }
  139. }
  140. }
  141. /**
  142. * Error with configuration
  143. */
  144. export class ConfigError extends CodeGraphError {
  145. constructor(message: string, details?: Record<string, unknown>) {
  146. super(message, 'CONFIG_ERROR', details);
  147. this.name = 'ConfigError';
  148. }
  149. }
  150. /**
  151. * A refused path — the caller asked for something outside the project root, or
  152. * for a sensitive system directory. Deliberately a plain `Error` and NOT a
  153. * {@link CodeGraphError}: it is a security marker every read sink tests with
  154. * `instanceof`, not a categorized operational failure, and the MCP layer treats
  155. * it as one of the only two "stop trying" conditions (see `mcp/tools.ts`).
  156. *
  157. * It lives here — in the dependency-free error module — rather than next to its
  158. * first caller so that a consumer can enforce the refusal WITHOUT importing the
  159. * MCP tool graph. `mcp/tools.ts` re-exports it, so the class identity stays
  160. * single and every existing `instanceof` check keeps working.
  161. */
  162. export class PathRefusalError extends Error {}
  163. /**
  164. * Simple logger for CodeGraph operations
  165. *
  166. * By default, logs to console.warn for warnings and console.error for errors.
  167. * Can be configured to use custom logging.
  168. */
  169. export interface Logger {
  170. debug(message: string, context?: Record<string, unknown>): void;
  171. warn(message: string, context?: Record<string, unknown>): void;
  172. error(message: string, context?: Record<string, unknown>): void;
  173. }
  174. /**
  175. * Default console-based logger
  176. */
  177. export const defaultLogger: Logger = {
  178. debug(message: string, context?: Record<string, unknown>): void {
  179. if (process.env.CODEGRAPH_DEBUG) {
  180. console.debug(`[CodeGraph] ${message}`, context ?? '');
  181. }
  182. },
  183. warn(message: string, context?: Record<string, unknown>): void {
  184. console.warn(`[CodeGraph] ${message}`, context ?? '');
  185. },
  186. error(message: string, context?: Record<string, unknown>): void {
  187. console.error(`[CodeGraph] ${message}`, context ?? '');
  188. },
  189. };
  190. /**
  191. * Silent logger (no output) - useful for tests
  192. */
  193. export const silentLogger: Logger = {
  194. debug(): void {},
  195. warn(): void {},
  196. error(): void {},
  197. };
  198. /**
  199. * Current logger instance (can be replaced)
  200. */
  201. let currentLogger: Logger = defaultLogger;
  202. /**
  203. * Set the global logger
  204. */
  205. export function setLogger(logger: Logger): void {
  206. currentLogger = logger;
  207. }
  208. /**
  209. * Get the current logger
  210. */
  211. export function getLogger(): Logger {
  212. return currentLogger;
  213. }
  214. /**
  215. * Log a debug message
  216. */
  217. export function logDebug(message: string, context?: Record<string, unknown>): void {
  218. currentLogger.debug(message, context);
  219. }
  220. /**
  221. * Log a warning message
  222. */
  223. export function logWarn(message: string, context?: Record<string, unknown>): void {
  224. currentLogger.warn(message, context);
  225. }
  226. /**
  227. * Log an error message
  228. */
  229. export function logError(message: string, context?: Record<string, unknown>): void {
  230. currentLogger.error(message, context);
  231. }