// Node-flavoured error mapping, shared by the runtime (guest-facing fs/loader // errors) and the host (fs facade rejections). Lives in shared/ so both sides // map one kernel FsError identically — one source of truth for code/errno/ // syscall/path, per the FS slice brief. import type { FsError } from "./protocol"; const ERRNO_TEXT: Record = { ENOENT: "no such file or directory", EEXIST: "file already exists", ENOTDIR: "not a directory", EISDIR: "illegal operation on a directory", EBADF: "bad file descriptor", EINVAL: "invalid argument", ENOTEMPTY: "directory not empty", EIO: "i/o error", EMFILE: "too many open files", EAGAIN: "resource temporarily unavailable", EADDRINUSE: "address already in use", ELOOP: "too many symbolic links encountered", }; const FS_ERRNO_NUMBER: Record = { EPERM: 1, ENOENT: 2, ESRCH: 3, EINTR: 4, EIO: 5, EEXIST: 17, EBADF: 9, EAGAIN: 11, ENOMEM: 12, EACCES: 13, EFAULT: 14, EBUSY: 16, ENOTDIR: 20, EISDIR: 21, EINVAL: 22, EMFILE: 24, ENOSPC: 28, ENOTEMPTY: 39, ELOOP: 40, EADDRINUSE: 98, }; /** A guest-facing fs error exactly as Node's uvException builds it: message * `CODE: text, syscall 'path' -> 'dest'`, a negative errno, and own keys in Node's * order (errno, code, syscall, path, dest). */ export function uvError(e: FsError): Error { const text = ERRNO_TEXT[e.code] ?? "unknown error"; let message = `${e.code}: ${text}, ${e.syscall}`; if (e.path !== undefined) message += ` '${e.path}'`; if (e.dest !== undefined) message += ` -> '${e.dest}'`; const fields: Record = { errno: -(FS_ERRNO_NUMBER[e.code] ?? 0), code: e.code, syscall: e.syscall }; if (e.path !== undefined) fields.path = e.path; if (e.dest !== undefined) fields.dest = e.dest; return Object.assign(new Error(message), fields); } /** Map a kernel FsError into a Node-flavoured Error with a numeric errno. */ export function toNodeError(e: FsError): Error { const text = ERRNO_TEXT[e.code] ?? "unknown error"; const errno = FS_ERRNO_NUMBER[e.code] ?? 0; const error = new Error(`${e.code}: ${text}${e.path ? `, ${e.syscall ?? "syscall"} '${e.path}'` : ""}`); (error as Error & { code?: string; errno?: number; syscall?: string; path?: string }).code = e.code; (error as Error & { errno?: number }).errno = errno; (error as Error & { syscall?: string }).syscall = e.syscall; if (e.path) (error as Error & { path?: string }).path = e.path; return error; }