import { Error, ObjectCreate, ObjectDefineProperty as define, ObjectFreeze as freeze, ReflectConstruct as construct, WeakMap, WeakMapPrototypeGet as get, WeakMapPrototypeSet as set, } from "ext:core/primordials"; export type FileReply = | [value: T, error: null] | [value: null, error: [kind: string, message: string]]; type FileErrorName = | "NotFound" | "PermissionDenied" | "AlreadyExists" | "NotEmpty" | "CrossDevice" | "NotADirectory" | "IsADirectory" | "InvalidInput" | "BadResource" | "Busy" | "BrokenPipe" | "NotSupported" | "IoError"; type FileErrorConstructor = new (message?: string) => Error; const errors = ObjectCreate(null) as Readonly>; const codes = new WeakMap(); export function errorCode(error: unknown): string | undefined { return get(codes, error as object); } for ( const name of [ "NotFound", "PermissionDenied", "AlreadyExists", "NotEmpty", "CrossDevice", "NotADirectory", "IsADirectory", "InvalidInput", "BadResource", "Busy", "BrokenPipe", "NotSupported", "IoError", ] ) { const ctor = class extends Error { // @ts-expect-error Reflect.construct avoids a guest-replaceable superclass constructor. constructor(message = "") { const self = construct(Error, [message], new.target) as Error; define(self, "name", { value: name, writable: true, configurable: true }); return self; } }; define(ctor, "name", { value: name, configurable: true }); define(errors, name, { value: ctor, enumerable: true }); } freeze(errors); export { errors }; export function failure(name: string, message: string): Error { const result = construct( Error, [message], (errors as Record)[name] || errors.IoError, ) as Error; const descriptor: { value: string; writable: boolean; configurable: boolean; } = ObjectCreate(null); descriptor.value = name; descriptor.writable = true; descriptor.configurable = true; define(result, "name", descriptor); set(codes, result, name); return result; } export function result(reply: FileReply): T { if (reply[1] !== null) throw failure(reply[1][0], reply[1][1]); return reply[0]; }