asatilloalisherov029-eng/Cefrcenter

GitHub: asatilloalisherov029-eng/Cefrcenter

将 Zod 校验错误包装为用户友好的可读消息的 TypeScript 工具库,同时保留原始错误细节供开发者调试使用。

Stars: 0 | Forks: 0

# zod-validation-error 将 zod 校验错误包装为用户友好的可读消息。 [![Build Status](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/causaly/zod-validation-error/actions/workflows/ci.yml) [![npm version](https://img.shields.io/npm/v/zod-validation-error.svg?color=0c0)](https://www.npmjs.com/package/zod-validation-error) #### 特性 - 用户友好的可读错误消息,提供丰富的配置选项; - 保留原始错误细节,可通过 `error.details` 访问; - 提供自定义 error map 以实现更好的用户友好消息; - 同时支持 Zod v3 和 v4。 **_注意:_** 当前版本的 `zod-validation-error` 适用于 zod v4。如果您正在寻找 zod v3 的支持,请参阅 [v3 文档](./README.v3.md) ## 安装 ``` npm install zod-validation-error ``` #### 环境要求 - Node.js v.18+ - TypeScript v.4.5+ ## 快速开始 ``` import { z as zod } from 'zod'; import { fromError, createErrorMap } from 'zod-validation-error'; // configure zod to use zod-validation-error's error map // this is optional, you may also use your own custom error map or zod's native error map // we recommend using zod-validation-error's error map for better user-friendly messages // see https://zod.dev/error-customization for further details zod.config({ customError: createErrorMap(), }); // create zod schema const zodSchema = zod.object({ id: zod.int().positive(), email: zod.email(), }); // parse some invalid value try { zodSchema.parse({ id: 1, email: 'coyote@acme', // note: invalid email }); } catch (err) { const validationError = fromError(err); // the error is now readable by the user // you may print it to console console.log(validationError.toString()); // or return it as an actual error return validationError; } ``` ## 动机 Zod 错误对于最终用户来说难以直接使用。本库将 Zod 校验错误包装为用户友好的可读消息,可以暴露给外部,同时将原始错误保留在数组中以供 _开发_ 使用。 ### 示例 #### 输入(来自 Zod) ``` [ { "origin": "number", "code": "too_small", "minimum": 0, "inclusive": false, "path": ["id"], "message": "Number must be greater than 0 at \"id\"" }, { "origin": "string", "code": "invalid_format", "format": "email", "pattern": "/^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$/", "path": ["email"], "message": "Invalid email at \"email\"" } ] ``` #### 输出 ``` Validation error: Number must be greater than 0 at "id"; Invalid email at "email" ``` ## API - [ValidationError(message[, options])](#validationerror) - [createErrorMap(options)](#createErrorMap) - [createMessageBuilder(options)](#createMessageBuilder) - [isValidationError(error)](#isvalidationerror) - [isValidationErrorLike(error)](#isvalidationerrorlike) - [isZodErrorLike(error)](#iszoderrorlike) - [fromError(error[, options])](#fromerror) - [fromZodIssue(zodIssue[, options])](#fromzodissue) - [fromZodError(zodError[, options])](#fromzoderror) - [toValidationError([options]) => (error) => ValidationError](#tovalidationerror) ### ValidationError 主要的 `ValidationError` 类,继承了 JavaScript 原生的 `Error`。 #### 参数 - `message` - _string_;错误消息(必填) - `options` - _ErrorOptions_;根据 [JavaScript 定义](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Error#options) 的错误选项(可选) - `options.cause` - _any_;可用于保存原始的 zod 错误(可选) #### 示例 1:使用 `message` 构造新的 ValidationError ``` import { ValidationError } from 'zod-validation-error'; const error = new ValidationError('foobar'); console.log(error instanceof Error); // prints true ``` #### 示例 2:使用 `message` 和 `options.cause` 构造新的 ValidationError ``` import { z as zod } from 'zod'; import { ValidationError } from 'zod-validation-error'; const error = new ValidationError('foobar', { cause: new zod.ZodError([ { origin: 'number', code: 'too_small', minimum: 0, inclusive: false, path: ['id'], message: 'Number must be greater than 0 at "id"', input: -1, }, ]), }); console.log(error.details); // prints issues from zod error ``` ### createErrorMap 创建 zod-validation-error 的 `errorMap`,用于将 issue 格式化为用户友好的错误消息。 我们认为 zod 原生的 error map 对用户不够友好,因此我们提供了自己的实现,将 issue 格式化为人类可读的消息。 注意:zod-validation-error 的 `errorMap` 与其他所有 error map 一样,也可以直接在 `zod` 中使用(有关更多详细信息,请参阅 https://zod.dev/error-customization),例如: #### 参数 - `options` - _Object_;格式化选项(可选) ##### createErrorMap 选项 | 名称 | 类型 | 描述 | | ------------------------------- | :-------------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `displayInvalidFormatDetails` | `boolean` | 指示是否在错误消息中显示无效格式的详细信息(例如 regexp pattern)(可选,默认为 `false`) | | `maxAllowedValuesToDisplay` | `number` | 要显示的允许值的最大数量(可选,默认为 `10`)。超出此限制的允许值将被隐藏。 | | `allowedValuesSeparator` | `string` | 用于在消息中连接允许值的分隔符(可选,默认为 `", "`) | | `allowedValuesLastSeparator` | `string \| undefined` | 用于在消息中连接最后一个允许值的分隔符(可选,默认为 `" or "`)。设置为 `undefined` 可禁用。 | | `wrapAllowedValuesInQuote` | `boolean` | 指示是否用引号将允许值包裹起来(可选,默认为 `true`)。请注意,这仅适用于字符串值。 | | `maxUnrecognizedKeysToDisplay` | `number` | 要在错误消息中显示的无法识别的 key 的最大数量(可选,默认为 `5`) | | `unrecognizedKeysSeparator` | `string` | 用于在消息中连接无法识别的 key 的分隔符(可选,默认为 `", "`) | | `unrecognizedKeysLastSeparator` | `string \| undefined` | 用于在消息中连接最后一个无法识别的 key 的分隔符(可选,默认为 `" and "`)。设置为 `undefined` 可禁用。 | | `wrapUnrecognizedKeysInQuote` | `boolean` | 指示是否用引号将无法识别的 key 包裹起来(可选,默认为 `true`)。请注意,这仅适用于字符串 key。 | | `dateLocalization` | `boolean \| Intl.LocalesArgument` | 指示是否对日期值进行本地化(可选,默认为 `true`)。如果设置为 `true`,将使用环境的默认区域设置。您也可以传入 `Intl.LocalesArgument` 来指定自定义区域设置。 | | `numberLocalization` | `boolean \| Intl.LocalesArgument` | 指示是否对数值进行本地化(可选,默认为 `true`)。如果设置为 `true`,将使用环境的默认区域设置。您也可以传入 `Intl.LocalesArgument` 来指定自定义区域设置。 | #### 示例 ``` import { z as zod } from 'zod'; import { createErrorMap } from 'zod-validation-error'; zod.config({ customError: createErrorMap({ // default values are used when not specified displayInvalidFormatDetails: true, }), }); ``` ### createMessageBuilder 创建 zod-validation-error 的默认 `MessageBuilder`,用于生成用户友好的错误消息。 旨在作为选项传递给 [fromError](#fromerror)、[fromZodIssue](#fromzodissue)、[fromZodError](#fromzoderror) 或 [toValidationError](#tovalidationerror)。 #### 参数 - `options` - _Object_;格式化选项(可选) ##### createMessageBuilder 选项 | 名称 | 类型 | 描述 | | -------------------- | :-------------------: | ---------------------------------------------------------------------------------------------------------------------------------------- | | `maxIssuesInMessage` | `number` | 包含在用户友好消息中的最大 issue 数量(可选,默认为 `99`) | | `issueSeparator` | `string` | 用于在用户友好消息中连接 issue 的分隔符(可选,默认为 `";"`) | | `unionSeparator` | `string` | 用于在用户友好消息中连接 union-issue 的分隔符(可选,默认为 `" or "`) | | `prefix` | `string \| undefined` | 在用户友好消息中使用的前缀(可选,默认为 `"Validation error"`)。传入 `undefined` 可完全禁用前缀。 | | `prefixSeparator` | `string` | 用于将前缀与用户友好消息的其余部分连接起来的分隔符(可选,默认为 `": "`)。当 `prefix` 为 `undefined` 时不使用。 | | `includePath` | `boolean` | 指示是否在错误消息中包含出错的属性 key(可选,默认为 `true`) | | `forceTitleCase` | `boolean` | 指示是否将单个 issue 消息转换为 title case(可选,默认为 `true`)。 | #### 示例 ``` import { createMessageBuilder } from 'zod-validation-error'; const messageBuilder = createMessageBuilder({ maxIssuesInMessage: 3, includePath: false, }); ``` ### isValidationError 一个[类型保护](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)工具函数,基于 `instanceof` 比较。 #### 参数 - `error` - 错误实例(必填) #### 示例 ``` import { z as zod } from 'zod'; import { ValidationError, isValidationError } from 'zod-validation-error'; const err = new ValidationError('foobar'); isValidationError(err); // returns true const invalidErr = new Error('foobar'); isValidationError(err); // returns false ``` ### isValidationErrorLike 一个[类型保护](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)工具函数,基于 _启发式_ 比较。 _既然我们可以使用简单的 `instanceof` 比较,为什么还需要启发式方法?_ 因为多版本不一致性的问题。例如,某个依赖项可能在内部使用了较旧版本的 `zod-validation-error`。在这种情况下,`instanceof` 比较将产生无效结果,因为模块去重不适用于 npm/yarn 层面,并且原型不同。 长话短说:如果您不确定,最好使用 `isValidationErrorLike` 而不是 `isValidationError`。 #### 参数 - `error` - 错误实例(必填) #### 示例 ``` import { ValidationError, isValidationErrorLike } from 'zod-validation-error'; const err = new ValidationError('foobar'); isValidationErrorLike(err); // returns true const invalidErr = new Error('foobar'); isValidationErrorLike(err); // returns false ``` ### isZodErrorLike 一个[类型保护](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)工具函数,基于 _启发式_ 比较。 _既然我们可以使用简单的 `instanceof` 比较,为什么还需要启发式方法?_ 因为多版本不一致性的问题。例如,某个依赖项可能在内部使用了较旧版本的 `zod`。在这种情况下,`instanceof` 比较将产生无效结果,因为模块去重不适用于 npm/yarn 层面,并且原型不同。 #### 参数 - `error` - 错误实例(必填) #### 示例 ``` import { z as zod } from 'zod'; import { ValidationError, isZodErrorLike } from 'zod-validation-error'; const zodValidationErr = new ValidationError('foobar'); isZodErrorLike(zodValidationErr); // returns false const genericErr = new Error('foobar'); isZodErrorLike(genericErr); // returns false const zodErr = new zod.ZodError([ { origin: 'number', code: 'too_small', minimum: 0, inclusive: false, path: ['id'], message: 'Number must be greater than 0 at "id"', input: -1, }, ]); isZodErrorLike(zodErr); // returns true ``` ### fromError 将错误转换为 `ValidationError`。 _`fromError` 和 `fromZodError` 有什么区别?_ `fromError` 函数是 `fromZodError` 的一个较宽松版本。它可以接受未知错误并尝试将其转换为 `ValidationError`。 #### 参数 - `error` - _unknown_;一个错误(必填) - `options` - _Object_;格式化选项(可选) - `messageBuilder` - _MessageBuilder_;一个接受 `zod.ZodIssue` 对象数组并返回 `string` 形式的用户友好错误消息的函数(可选)。 #### 注意 或者,您可以直接将 [createMessageBuilder 选项](#createmessagebuilder-options) 作为 `options` 传递。这些将用作在内部创建 `MessageBuilder` 实例的参数。 ### fromZodIssue 将单个 zod issue 转换为 `ValidationError`。 #### 参数 - `zodIssue` - _zod.ZodIssue_;一个 ZodIssue 实例(必填) - `options` - _Object_;格式化选项(可选) - `messageBuilder` - _MessageBuilder_;一个接受 `zod.ZodIssue` 对象数组并返回 `string` 形式的用户友好错误消息的函数(可选)。 #### 注意 或者,您可以直接将 [createMessageBuilder 选项](#createmessagebuilder-options) 作为 `options` 传递。这些将用作在内部创建 `MessageBuilder` 实例的参数。 ### fromZodError 将 zod 错误转换为 `ValidationError`。 _`ZodError` 和 `ZodIssue` 有什么区别?_ `ZodError` 是 1 个或多个 `ZodIssue` 实例的集合。这是您在调用 `zodSchema.parse()` 时得到的结果。 #### 参数 - `zodError` - _zod.ZodError_;一个 ZodError 实例(必填) - `options` - _Object_;格式化选项(可选) - `messageBuilder` - _MessageBuilder_;一个接受 `zod.ZodIssue` 对象数组并返回 `string` 形式的用户友好错误消息的函数(可选)。 #### 注意 或者,您可以直接将 [createMessageBuilder 选项](#createmessagebuilder-optionscreateMessageBuilder) 作为 `options` 传递。这些将用作在内部创建 `MessageBuilder` 实例的参数。 ### toValidationError `fromZodError` 的柯里化版本,专为 FP(函数式编程)设计。请注意,如果需要,它首先接受 options 对象,并返回一个将 `zodError` 转换为 `ValidationError` 对象的函数。 ``` toValidationError(options) => (zodError) => ValidationError ``` #### 使用 fp-ts 的示例 ``` import * as Either from 'fp-ts/Either'; import { z as zod } from 'zod'; import { toValidationError, ValidationError } from 'zod-validation-error'; // create zod schema const zodSchema = zod .object({ id: zod.int().positive(), email: zod.email(), }) .brand<'User'>(); export type User = zod.infer; export function parse( value: zod.input ): Either.Either { return Either.tryCatch(() => schema.parse(value), toValidationError()); } ``` ## 常见问题 ### zod-validation-error 和 zod 自带的 [prettifyError](https://zod.dev/error-formatting#zprettifyerror) 有什么区别? 虽然两个库都旨在提供 zod 错误的人类可读字符串表示形式,但它们在几个方面有所不同... 1. **关注最终用户**:zod-validation-error 提供了固执己见的、用户友好的错误消息,旨在直接在表单或 API 响应中显示给最终用户。 2. **自定义选项**:zod-validation-error 提供了丰富的消息格式化配置,例如控制路径包含、允许值显示、本地化等。 3. **错误处理**:zod-validation-error 在通过 ValidationError 类提供干净、一致的接口的同时,保留了原始错误细节。 4. **集成灵活性**:除了格式化之外,zod-validation-error 还提供了适用于各种架构模式(例如函数式编程)的错误检测和转换工具函数。 免责声明:根据此[评论](https://github.com/causaly/zod-validation-error/issues/455#issuecomment-2811895152),我们无意与 zod 对抗。事实上,如果符合社区的最大利益,我们很乐意废弃此模块。就目前而言,似乎 `od-validation-error` 和 `prettifyError` 都有各自的生存空间,这也是基于 Colin McDonnell 的[回复](https://github.com/causaly/zod-validation-error/issues/455#issuecomment-2814466019)。 ### 我需要使用 `zod-validation-error` 的 error map 吗? 不需要,如果您愿意,可以使用 zod 原生的 error map。但是,我们建议使用 `zod-validation-error` 的 error map 以获得更好的用户友好消息。 如果您有特定需求(例如国际化),也可以使用您自己的自定义 error map。 ### 我在哪里可以看到 `zod-validation-error` 的 error map 格式化是如何工作的? 了解 `zod-validation-error` 的 error map 工作原理的最简单方法是查看[测试](./lib/v4/errorMap/errorMap.test.ts)。它们涵盖了各种场景,并展示了 error map 如何将 issue 格式化为用户友好的消息。 ### 如何区分不同的错误 使用 `isValidationErrorLike` 类型保护。 #### 示例 场景:区分 `ValidationError` 和普通的 `Error`,以便分别响应 400 和 500 HTTP 状态码。 ``` import { isValidationErrorLike } from 'zod-validation-error'; try { func(); // throws Error - or - ValidationError } catch (err) { if (isValidationErrorLike(err)) { return 400; // Bad Data (this is a client error) } return 500; // Server Error } ``` ### 如何在 `zod` 之外使用 `ValidationError` 可以在 `zod` 之外实现自定义校验逻辑并抛出 `ValidationError`。 #### 示例 1:传递自定义消息 ``` import { ValidationError } from 'zod-validation-error'; import { Buffer } from 'node:buffer'; function parseBuffer(buf: unknown): Buffer { if (!Buffer.isBuffer(buf)) { throw new ValidationError('Invalid argument; expected buffer'); } return buf; } ``` #### 示例 2:传递自定义消息和原始错误作为 cause ``` import { ValidationError } from 'zod-validation-error'; try { // do something that throws an error } catch (err) { throw new ValidationError('Something went deeply wrong', { cause: err }); } ``` ### 如何将 `ValidationError` 与自定义 "error map" 结合使用 Zod 支持通过提供自定义 "error map" 来自定义错误消息。您可以将其与 `zod-validation-error` 结合使用以生成用户友好的消息。 #### 示例:使用 `customError` 属性生成用户友好的错误消息 如果您只需要生成用户友好的错误消息,可以使用 `customError` 属性。 ``` import { z as zod } from 'zod'; import { createErrorMap } from 'zod-validation-error'; zod.config({ customError: createErrorMap({ includePath: true, }), }); ``` 当设置了该属性时,`zod-validation-error` 将遵循 `customError` 属性,无需进一步配置。 ### `zod-validation-error` 支持 CommonJS 吗 是的,`zod-validation-error` 开箱即用地支持 CommonJS。您只需要使用 `require` 导入它即可。 #### 示例 ``` const { ValidationError } = require('zod-validation-error'); ``` ## 贡献 非常欢迎贡献源代码。请提交 PR,确保 linter 通过并且所有测试都已通过。 #### 我们正在招聘 Causaly 正在使用 TypeScript、React 和 Node.js 等技术构建世界上最大的生物医学知识平台。在 https://jobs.ashbyhq.com/causaly 了解更多关于我们的招聘职位。 ## 许可证 MIT
标签:MITM代理, Syscall, TypeScript, Web开发, Zod, 安全插件, 数据可视化, 数据验证, 暗色界面, 错误处理