import { defineCodes, SuaveplanError, type SuaveplanErrorOptions, } from "@suaveplan/error"; /** * Stable error code registry for the `@suaveplan/surrealdb` package. * All codes are prefixed with `SURREAL.` to avoid collisions with other * packages in the same process. */ export const SURREAL_CODES = defineCodes("SURREAL", [ "CONNECTION_FAILED", "NOT_CONNECTED", "QUERY_FAILED", "AUTH_FAILED", "TIMEOUT", "VERSION_UNSUPPORTED", "SCHEMA_LOAD_FAILED", "INIT_FAILED", "TRANSACTION_FAILED", "RECORD_NOT_FOUND", ] as const); export type SurrealCode = (typeof SURREAL_CODES)[keyof typeof SURREAL_CODES]; /** * Structured error class for all SurrealDB adapter failures. * * Extends `SuaveplanError` and carries a stable `SurrealCode` discriminant * so callers can match errors without string comparison. */ export class SurrealError extends SuaveplanError { public constructor( code: SurrealCode, message: string, options?: SuaveplanErrorOptions ) { // Normalise empty/whitespace messages so downstream log surfaces // (OTel attributes, console.error) always carry the stable code // rather than rendering an empty `Error: ` line. The public // constructor also widens the protected `SuaveplanError` // constructor for in-process callers. const normalised = message.trim().length > 0 ? message : `SurrealError(${code})`; super(code, normalised, options); this.name = "SurrealError"; } } /** * Map a raw driver error to the most appropriate `SurrealCode`. * * Returns `SURREAL.QUERY_FAILED` as a safe fallback for unrecognised inputs. */ export function mapSurrealCode(err: unknown): SurrealCode { if (err instanceof SurrealError) return err.code; const msg = err instanceof Error ? err.message.toLowerCase() : String(err).toLowerCase(); if ( msg.includes("connect") || msg.includes("connection") || msg.includes("econnrefused") || msg.includes("connectionrefused") ) { return SURREAL_CODES.CONNECTION_FAILED; } if (msg.includes("auth") || msg.includes("wrong credentials")) { return SURREAL_CODES.AUTH_FAILED; } if (msg.includes("timeout") || msg.includes("timed out")) { return SURREAL_CODES.TIMEOUT; } if (msg.includes("not found") || msg.includes("does not exist")) { return SURREAL_CODES.RECORD_NOT_FOUND; } if (msg.includes("transaction")) { return SURREAL_CODES.TRANSACTION_FAILED; } return SURREAL_CODES.QUERY_FAILED; }