CRES
common-resolver first transforms each raw error object into a CEF (Common ErrorObject Format). This normalizes different error shapes into a consistent structure, and then converts that structure into a user-friendly CRES (Common-Resolver ErrorObject Structure). These converters are defined inside each resolver, and the CEF has the form { [path: string]: string }.
CRES is the canonical error response format provided by common-resolver. It standardizes errors from various sources, allowing developers to manage disparate error shapes without writing custom parsing logic. Given an input value and the schema that validates it, CRES shows where errors occurred in a predictable layout.
CRES is shaped as shown below. Each entry may include a root property that represents an error for the object or array itself. If root is omitted, it can be difficult to express a top-level object error for nested entries like agreeToTerms.marketingTerms. The root field allows conveying both specific sub-property errors and general object-level errors, resulting in simpler and more flexible error handling.
common-resolver wraps CRES with a Proxy object. The Proxy does several helpful things: when accessing a path it can automatically return a root value for that path, and if a nested object exists it will return another Proxy so accesses are recursive. This lets developers navigate nested error structures without checking each level manually. The Proxy also prevents modifications or deletions of error properties, preserving the immutability of error messages. In cases where a property is present, the Proxy may synthesize a virtual entry to make lookups consistent and convenient.
Regarding the CRES type definition: only the top level may optionally contain root, while nested entries are treated as if they do not have root. At runtime TypeScript may consider the root field to be an object instead of a string, which is inconvenient when trying to read it directly. Although not ideal, the pragmatic approach is to use an explicit cast (as string) when accessing root values.

Consider a modification to RecursivePartial below. With this shape, deep property access using dot notation requires checking whether each step is a string or an object, otherwise TypeScript will complain. Rather than encoding precise runtime shapes, the practical choice is to assert the root value as string at the call site to avoid repeated type checks.
