refactor(core): migrate MessageV2 errors to Schema-backed named errors (#23764)

This commit is contained in:
Kit Langton
2026-04-21 23:40:32 -04:00
committed by GitHub
parent 1593c3ed16
commit ed802fd121
3 changed files with 86 additions and 33 deletions

View File

@@ -0,0 +1,59 @@
import { Schema } from "effect"
import z from "zod"
import { zod } from "@/util/effect-zod"
/**
* Create a Schema-backed NamedError-shaped class.
*
* Drop-in replacement for `NamedError.create(tag, zodShape)` but backed by
* `Schema.Struct` under the hood. The wire shape emitted by the derived
* `.Schema` is still `{ name: tag, data: {...fields} }` so the generated
* OpenAPI/SDK output is byte-identical to the original NamedError schema.
*
* Preserves the existing surface:
* - static `Schema` (Zod schema of the wire shape)
* - static `isInstance(x)`
* - instance `toObject()` returning `{ name, data }`
* - `new X({ ...data }, { cause })`
*/
export function namedSchemaError<Tag extends string, Fields extends Schema.Struct.Fields>(tag: Tag, fields: Fields) {
// Wire shape matches the original NamedError output so the SDK stays stable.
const dataSchema = Schema.Struct(fields)
const wire = z
.object({
name: z.literal(tag),
data: zod(dataSchema),
})
.meta({ ref: tag })
type Data = Schema.Schema.Type<typeof dataSchema>
class NamedSchemaError extends Error {
static readonly Schema = wire
static readonly tag = tag
public static isInstance(input: unknown): input is NamedSchemaError {
return (
typeof input === "object" &&
input !== null &&
"name" in input &&
(input as { name: unknown }).name === tag
)
}
public override readonly name: Tag = tag
public readonly data: Data
constructor(data: Data, options?: ErrorOptions) {
super(tag, options)
this.data = data
}
toObject(): { name: Tag; data: Data } {
return { name: tag, data: this.data }
}
}
Object.defineProperty(NamedSchemaError, "name", { value: tag })
return NamedSchemaError
}