zod-validation-error
Advanced tools
Comparing version
{ | ||
"name": "zod-validation-error", | ||
"version": "4.0.0-beta.2", | ||
"version": "4.0.0", | ||
"description": "Wrap zod validation errors in user-friendly readable messages", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
115
README.md
@@ -11,6 +11,6 @@ # zod-validation-error | ||
- Preserves original error details accessible via `error.details`; | ||
- Provides a custom error map for automatic message formatting; | ||
- Provides a custom error map for better user-friendly messages; | ||
- Supports both Zod v3 and v4. | ||
**_Note:_** This version of `zod-validation-error` works with zod v4. If you are looking for zod v3 support, please click [here](./README.v3.md) | ||
**_Note:_** This version of `zod-validation-error` works with zod v4. If you are looking for zod v3 support, please refer to the [v3 documentation](./README.v3.md) | ||
@@ -31,5 +31,13 @@ ## Installation | ||
```typescript | ||
import { z as zod } from 'zod/v4'; | ||
import { fromError } from 'zod-validation-error'; | ||
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 | ||
@@ -107,3 +115,3 @@ const zodSchema = zod.object({ | ||
Main `ValidationError` class, extending native JavaScript `Error`. | ||
Main `ValidationError` class, extending JavaScript's native `Error`. | ||
@@ -128,3 +136,3 @@ #### Arguments | ||
```typescript | ||
import { z as zod } from 'zod/v4'; | ||
import { z as zod } from 'zod'; | ||
import { ValidationError } from 'zod-validation-error'; | ||
@@ -153,5 +161,5 @@ | ||
Meant to be passed as an option to [fromError](#fromerror), [fromZodIssue](#fromzodissue), [fromZodError](#fromzoderror), [toValidationError](#tovalidationerror) or [MessageBuilder](#MessageBuilder). | ||
We think that zod's native error map is not user-friendly enough, so we provide our own implementation that formats issues into human-readable messages. | ||
Note: zod-validation-error's `errorMap` is an errorMap like all others and thus can also be used directly with `zod` (see https://zod.dev/error-customization for further details). | ||
Note: zod-validation-error's `errorMap` is an errorMap like all others and thus can also be used directly with `zod` (see https://zod.dev/error-customization for further details), e.g. | ||
@@ -164,19 +172,15 @@ #### Arguments | ||
| Name | Type | Description | | ||
| ------------------------------- | :-------------------------------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `includePath` | `boolean` | Indicates whether to include the erroneous property key in the error message (optional, defaults to `true`) | | ||
| `displayInvalidFormatDetails` | `boolean` | Indicates whether to display invalid format details (e.g. regexp pattern) in the error message (optional, defaults to `false`) | | ||
| `maxAllowedValuesToDisplay` | `number` | Max number of allowed values to display (optional, defaults to 10) | | ||
| `allowedValuesSeparator` | `string` | Used to concatenate allowed values in the message (optional, defaults to `", "`) | | ||
| `allowedValuesLastSeparator` | `string \| undefined` | Used to concatenate last allowed value in the message (optional, defaults to `" or "`). Set to `undefined` to disable last separator. | | ||
| `wrapAllowedValuesInQuote` | `boolean` | Indicates whether to wrap allowed values in quotes (optional, defaults to `true`). Note that this only applies to string values. | | ||
| `maxUnrecognizedKeysToDisplay` | `number` | Max number of unrecognized keys to display in the error message (optional, defaults to `5`) | | ||
| `unrecognizedKeysSeparator` | `string` | Used to concatenate unrecognized keys in the message (optional, defaults to `", "`) | | ||
| `unrecognizedKeysLastSeparator` | `string \| undefined` | Used to concatenate the last unrecognized key in message (optional, defaults to `" and "`). Set to `undefined` to disable last separator. | | ||
| `wrapUnrecognizedKeysInQuote` | `boolean` | Indicates whether to wrap unrecognized keys in quotes (optional, defaults to `true`). Note that this only applies to string keys. | | ||
| `issuesInTitleCase` | `boolean` | Indicates whether to convert issues to title case (optional, defaults to `true`). | | ||
| `unionSeparator` | `string` | Used to concatenate union-issues in user-friendly message (optional, defaults to `" or "`) | | ||
| `issueSeparator` | `string` | Used to concatenate issues in user-friendly message (optional, defaults to `";"`) | | ||
| `dateLocalization` | `boolean \| Intl.LocalesArgument` | Indicates whether to localize date values in the error message (optional, defaults to `true`). If set to `true`, it will use the default locale of the environment. You can also pass an `Intl.LocalesArgument` to specify a custom locale. | | ||
| `numberLocalization` | `boolean \| Intl.LocalesArgument` | Indicates whether to localize number values in the error message (optional, defaults to `true`). If set to `true`, it will use the default locale of the environment. You can also pass an `Intl.LocalesArgument` to specify a custom locale. | | ||
| Name | Type | Description | | ||
| ------------------------------- | :-------------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `displayInvalidFormatDetails` | `boolean` | Indicates whether to display invalid format details (e.g. regexp pattern) in the error message (optional, defaults to `false`) | | ||
| `maxAllowedValuesToDisplay` | `number` | Max number of allowed values to display (optional, defaults to `10`). Allowed values beyond this limit will be hidden. | | ||
| `allowedValuesSeparator` | `string` | Used to concatenate allowed values in the message (optional, defaults to `", "`) | | ||
| `allowedValuesLastSeparator` | `string \| undefined` | Used to concatenate last allowed value in the message (optional, defaults to `" or "`). Set to `undefined` to disable. | | ||
| `wrapAllowedValuesInQuote` | `boolean` | Indicates whether to wrap allowed values in quotes (optional, defaults to `true`). Note that this only applies to string values. | | ||
| `maxUnrecognizedKeysToDisplay` | `number` | Max number of unrecognized keys to display in the error message (optional, defaults to `5`) | | ||
| `unrecognizedKeysSeparator` | `string` | Used to concatenate unrecognized keys in the message (optional, defaults to `", "`) | | ||
| `unrecognizedKeysLastSeparator` | `string \| undefined` | Used to concatenate the last unrecognized key in message (optional, defaults to `" and "`). Set to `undefined` to disable. | | ||
| `wrapUnrecognizedKeysInQuote` | `boolean` | Indicates whether to wrap unrecognized keys in quotes (optional, defaults to `true`). Note that this only applies to string keys. | | ||
| `dateLocalization` | `boolean \| Intl.LocalesArgument` | Indicates whether to localize date values (optional, defaults to `true`). If set to `true`, it will use the default locale of the environment. You can also pass `Intl.LocalesArgument` to specify a custom locale. | | ||
| `numberLocalization` | `boolean \| Intl.LocalesArgument` | Indicates whether to localize numeric values (optional, defaults to `true`). If set to `true`, it will use the default locale of the environment. You can also pass `Intl.LocalesArgument` to specify a custom locale. | | ||
@@ -186,7 +190,10 @@ #### Example | ||
```typescript | ||
import { z as zod } from 'zod'; | ||
import { createErrorMap } from 'zod-validation-error'; | ||
const messageBuilder = createErrorMap({ | ||
includePath: false, | ||
maxAllowedValuesToDisplay: 3, | ||
zod.config({ | ||
customError: createErrorMap({ | ||
// default values are used when not specified | ||
displayInvalidFormatDetails: true, | ||
}), | ||
}); | ||
@@ -207,9 +214,11 @@ ``` | ||
| Name | Type | Description | | ||
| -------------------- | :-------------------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `maxIssuesInMessage` | `number` | Max issues to include in user-friendly message (optional, defaults to `99`) | | ||
| `issueSeparator` | `string` | Used to concatenate issues in user-friendly message (optional, defaults to `";"`) | | ||
| `prefix` | `string \| undefined` | Prefix to use in user-friendly message (optional, defaults to `"Validation error"`). Pass `undefined` to disable prefix completely. | | ||
| `prefixSeparator` | `string` | Used to concatenate prefix with rest of the user-friendly message (optional, defaults to `": "`). Not used when `prefix` is `undefined`. | | ||
| `error` | `ErrorMap \| false` | Accepts an `errorMap` to format individual issues into user-friendly error messages (optional). When set to `false` it will return the message as formatted by `zod`. Note that this is an optional property and if not provided, the default zod-validation-error error map will be used. You can use your own custom errorMap if you want. | | ||
| Name | Type | Description | | ||
| -------------------- | :-------------------: | ---------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `maxIssuesInMessage` | `number` | Max issues to include in user-friendly message (optional, defaults to `99`) | | ||
| `issueSeparator` | `string` | Used to concatenate issues in user-friendly message (optional, defaults to `";"`) | | ||
| `unionSeparator` | `string` | Used to concatenate union-issues in user-friendly message (optional, defaults to `" or "`) | | ||
| `prefix` | `string \| undefined` | Prefix to use in user-friendly message (optional, defaults to `"Validation error"`). Pass `undefined` to disable prefix completely. | | ||
| `prefixSeparator` | `string` | Used to concatenate prefix with rest of the user-friendly message (optional, defaults to `": "`). Not used when `prefix` is `undefined`. | | ||
| `includePath` | `boolean` | Indicates whether to include the erroneous property key in the error message (optional, defaults to `true`) | | ||
| `forceTitleCase` | `boolean` | Indicates whether to convert individual issue messages to title case (optional, defaults to `true`). | | ||
@@ -219,9 +228,7 @@ #### Example | ||
```typescript | ||
import { createErrorMap, createMessageBuilder } from 'zod-validation-error'; | ||
import { createMessageBuilder } from 'zod-validation-error'; | ||
const messageBuilder = createMessageBuilder({ | ||
maxIssuesInMessage: 3, | ||
error: createErrorMap({ | ||
includePath: false, | ||
}), | ||
includePath: false, | ||
}); | ||
@@ -241,3 +248,3 @@ ``` | ||
```typescript | ||
import { z as zod } from 'zod/v4'; | ||
import { z as zod } from 'zod'; | ||
import { ValidationError, isValidationError } from 'zod-validation-error'; | ||
@@ -289,3 +296,3 @@ | ||
```typescript | ||
import { z as zod } from 'zod/v4'; | ||
import { z as zod } from 'zod'; | ||
import { ValidationError, isZodErrorLike } from 'zod-validation-error'; | ||
@@ -327,3 +334,3 @@ | ||
Alternatively, you may pass [createMessageBuilder options](#createMessageBuilder) directly as `options`. These will be used as arguments to create the `MessageBuilder` instance internally. | ||
Alternatively, you may pass [createMessageBuilder options](#createmessagebuilder-options) directly as `options`. These will be used as arguments to create the `MessageBuilder` instance internally. | ||
@@ -342,3 +349,3 @@ ### fromZodIssue | ||
Alternatively, you may pass [createMessageBuilder options](#createMessageBuilder) directly as `options`. These will be used as arguments to create the `MessageBuilder` instance internally. | ||
Alternatively, you may pass [createMessageBuilder options](#createmessagebuilder-options) directly as `options`. These will be used as arguments to create the `MessageBuilder` instance internally. | ||
@@ -359,3 +366,3 @@ ### fromZodError | ||
Alternatively, you may pass [createMessageBuilder options](#createMessageBuilder) directly as `options`. These will be used as arguments to create the `MessageBuilder` instance internally. | ||
Alternatively, you may pass [createMessageBuilder options](#createmessagebuilder-optionscreateMessageBuilder) directly as `options`. These will be used as arguments to create the `MessageBuilder` instance internally. | ||
@@ -374,3 +381,3 @@ ### toValidationError | ||
import * as Either from 'fp-ts/Either'; | ||
import { z as zod } from 'zod/v4'; | ||
import { z as zod } from 'zod'; | ||
import { toValidationError, ValidationError } from 'zod-validation-error'; | ||
@@ -401,3 +408,3 @@ | ||
1. **End-user focus**: zod-validation-error provides opinionated, user-friendly error messages designed to be displayed directly to end-users in forms or API responses, whereas Zod's native error handling seem more developer-focused. | ||
1. **End-user focus**: zod-validation-error provides opinionated, user-friendly error messages designed to be displayed directly to end-users in forms or API responses. | ||
1. **Customization options**: zod-validation-error offers extensive configuration for message formatting, such as controlling path inclusion, allowed values display, localization, and more. | ||
@@ -409,2 +416,12 @@ 1. **Error handling**: zod-validation-error maintains the original error details while providing a clean, consistent interface through the ValidationError class. | ||
### Do I need to use `zod-validation-error`'s error map? | ||
No, you can use zod's native error map if you prefer. However, we recommend using `zod-validation-error`'s error map for better user-friendly messages. | ||
You may also use your own custom error map if you have specific requirements, e.g. internationalization. | ||
### Where can I see how `zod-validation-error`'s error map formatting works? | ||
The easiest way to understand how `zod-validation-error`'s error map works is to look at the [tests](./lib/v4/errorMap/errorMap.test.ts). They cover various scenarios and demonstrate how the error map formats issues into user-friendly messages. | ||
### How to distinguish between errors | ||
@@ -472,4 +489,4 @@ | ||
```typescript | ||
import { z as zod } from 'zod/v4'; | ||
import { fromError, createErrorMap } from 'zod-validation-error'; | ||
import { z as zod } from 'zod'; | ||
import { createErrorMap } from 'zod-validation-error'; | ||
@@ -492,3 +509,3 @@ zod.config({ | ||
```typescript | ||
const { ValidationError } = require('zod-validation-error/v4'); | ||
const { ValidationError } = require('zod-validation-error'); | ||
``` | ||
@@ -495,0 +512,0 @@ |
@@ -29,3 +29,3 @@ # zod-validation-error | ||
```typescript | ||
import { z as zod } from 'zod'; | ||
import { z as zod } from 'zod/v3'; | ||
import { fromError } from 'zod-validation-error/v3'; | ||
@@ -123,3 +123,3 @@ | ||
```typescript | ||
import { z as zod } from 'zod'; | ||
import { z as zod } from 'zod/v3'; | ||
const { ValidationError } = require('zod-validation-error'); | ||
@@ -177,3 +177,3 @@ | ||
```typescript | ||
import { z as zod } from 'zod'; | ||
import { z as zod } from 'zod/v3'; | ||
import { errorMap } from 'zod-validation-error/v3'; | ||
@@ -195,3 +195,3 @@ | ||
```typescript | ||
import { z as zod } from 'zod'; | ||
import { z as zod } from 'zod/v3'; | ||
import { ValidationError, isValidationError } from 'zod-validation-error/v3'; | ||
@@ -246,3 +246,3 @@ | ||
```typescript | ||
import { z as zod } from 'zod'; | ||
import { z as zod } from 'zod/v3'; | ||
import { ValidationError, isZodErrorLike } from 'zod-validation-error/v3'; | ||
@@ -354,3 +354,3 @@ | ||
import * as Either from 'fp-ts/Either'; | ||
import { z as zod } from 'zod'; | ||
import { z as zod } from 'zod/v3'; | ||
import { toValidationError, ValidationError } from 'zod-validation-error/v3'; | ||
@@ -384,3 +384,3 @@ | ||
```typescript | ||
import { z as zod } from 'zod'; | ||
import { z as zod } from 'zod/v3'; | ||
import { type MessageBuilder, fromError } from 'zod-validation-error/v3'; | ||
@@ -439,3 +439,3 @@ import chalk from 'chalk'; | ||
import * as Either from 'fp-ts/Either'; | ||
import { z as zod } from 'zod'; | ||
import { z as zod } from 'zod/v3'; | ||
import { isValidationErrorLike } from 'zod-validation-error/v3'; | ||
@@ -494,3 +494,3 @@ | ||
```typescript | ||
import { z as zod } from 'zod'; | ||
import { z as zod } from 'zod/v3'; | ||
import { errorMap } from 'zod-validation-error/v3'; | ||
@@ -506,3 +506,3 @@ | ||
```typescript | ||
import { z as zod } from 'zod'; | ||
import { z as zod } from 'zod/v3'; | ||
import { fromZodIssue } from 'zod-validation-error/v3'; | ||
@@ -509,0 +509,0 @@ |
@@ -21,8 +21,4 @@ import * as zod from 'zod/v4/core'; | ||
type ErrorMapOptions = { | ||
includePath: boolean; | ||
dateLocalization: boolean | Intl.LocalesArgument; | ||
numberLocalization: boolean | Intl.LocalesArgument; | ||
unionSeparator: string; | ||
issueSeparator: string; | ||
issuesInTitleCase: boolean; | ||
displayInvalidFormatDetails: boolean; | ||
@@ -50,3 +46,5 @@ allowedValuesSeparator: string; | ||
issueSeparator: string; | ||
error: zod.$ZodErrorMap<zod.$ZodIssue> | false; | ||
unionSeparator: string; | ||
includePath: boolean; | ||
forceTitleCase: boolean; | ||
}; | ||
@@ -53,0 +51,0 @@ declare function createMessageBuilder(partialOptions?: Partial<MessageBuilderOptions>): MessageBuilder; |
348
v4/index.js
@@ -85,87 +85,11 @@ "use strict"; | ||
// lib/utils/stringify.ts | ||
function stringifySymbol(symbol) { | ||
return symbol.description ?? ""; | ||
// lib/v4/errorMap/custom.ts | ||
function parseCustomIssue(issue) { | ||
return { | ||
type: issue.code, | ||
path: issue.path, | ||
message: issue.message | ||
}; | ||
} | ||
function stringify(value, options = {}) { | ||
switch (typeof value) { | ||
case "symbol": | ||
return stringifySymbol(value); | ||
case "bigint": | ||
case "number": { | ||
switch (options.localization) { | ||
case true: | ||
return value.toLocaleString(); | ||
case false: | ||
return value.toString(); | ||
default: | ||
return value.toLocaleString(options.localization); | ||
} | ||
} | ||
case "string": { | ||
if (options.wrapStringValueInQuote) { | ||
return `"${value}"`; | ||
} | ||
return value; | ||
} | ||
default: { | ||
if (value instanceof Date) { | ||
switch (options.localization) { | ||
case true: | ||
return value.toLocaleString(); | ||
case false: | ||
return value.toISOString(); | ||
default: | ||
return value.toLocaleString(options.localization); | ||
} | ||
} | ||
return String(value); | ||
} | ||
} | ||
} | ||
// lib/utils/joinPath.ts | ||
var identifierRegex = /[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*/u; | ||
function joinPath(path) { | ||
if (path.length === 1) { | ||
let propertyKey = path[0]; | ||
if (typeof propertyKey === "symbol") { | ||
propertyKey = stringifySymbol(propertyKey); | ||
} | ||
return propertyKey.toString() || '""'; | ||
} | ||
return path.reduce((acc, propertyKey) => { | ||
if (typeof propertyKey === "number") { | ||
return acc + "[" + propertyKey.toString() + "]"; | ||
} | ||
if (typeof propertyKey === "symbol") { | ||
propertyKey = stringifySymbol(propertyKey); | ||
} | ||
if (propertyKey.includes('"')) { | ||
return acc + '["' + escapeQuotes(propertyKey) + '"]'; | ||
} | ||
if (!identifierRegex.test(propertyKey)) { | ||
return acc + '["' + propertyKey + '"]'; | ||
} | ||
const separator = acc.length === 0 ? "" : "."; | ||
return acc + separator + propertyKey; | ||
}, ""); | ||
} | ||
function escapeQuotes(str) { | ||
return str.replace(/"/g, '\\"'); | ||
} | ||
// lib/utils/NonEmptyArray.ts | ||
function isNonEmptyArray(value) { | ||
return value.length !== 0; | ||
} | ||
// lib/utils/titleCase.ts | ||
function titleCase(value) { | ||
if (value.length === 0) { | ||
return value; | ||
} | ||
return value.charAt(0).toUpperCase() + value.slice(1); | ||
} | ||
// lib/v4/errorMap/invalidElement.ts | ||
@@ -180,2 +104,11 @@ function parseInvalidElementIssue(issue) { | ||
// lib/v4/errorMap/invalidKey.ts | ||
function parseInvalidKeyIssue(issue) { | ||
return { | ||
type: issue.code, | ||
path: issue.path, | ||
message: `unexpected key in ${issue.origin}` | ||
}; | ||
} | ||
// lib/v4/errorMap/invalidStringFormat.ts | ||
@@ -322,2 +255,52 @@ function parseInvalidStringFormatIssue(issue, options = { | ||
// lib/v4/errorMap/invalidUnion.ts | ||
function parseInvalidUnionIssue(issue) { | ||
return { | ||
type: issue.code, | ||
path: issue.path, | ||
message: issue.message | ||
}; | ||
} | ||
// lib/utils/stringify.ts | ||
function stringifySymbol(symbol) { | ||
return symbol.description ?? ""; | ||
} | ||
function stringify(value, options = {}) { | ||
switch (typeof value) { | ||
case "symbol": | ||
return stringifySymbol(value); | ||
case "bigint": | ||
case "number": { | ||
switch (options.localization) { | ||
case true: | ||
return value.toLocaleString(); | ||
case false: | ||
return value.toString(); | ||
default: | ||
return value.toLocaleString(options.localization); | ||
} | ||
} | ||
case "string": { | ||
if (options.wrapStringValueInQuote) { | ||
return `"${value}"`; | ||
} | ||
return value; | ||
} | ||
default: { | ||
if (value instanceof Date) { | ||
switch (options.localization) { | ||
case true: | ||
return value.toLocaleString(); | ||
case false: | ||
return value.toISOString(); | ||
default: | ||
return value.toLocaleString(options.localization); | ||
} | ||
} | ||
return String(value); | ||
} | ||
} | ||
} | ||
// lib/utils/joinValues.ts | ||
@@ -397,3 +380,3 @@ function joinValues(values, options) { | ||
path: issue.path, | ||
message: `number must be less ${issue.inclusive ? "or equal to" : "than"} ${maxValueStr}` | ||
message: `number must be less than${issue.inclusive ? " or equal to" : ""} ${maxValueStr}` | ||
}; | ||
@@ -440,3 +423,3 @@ } | ||
path: issue.path, | ||
message: `value must be less ${issue.inclusive ? "or equal to" : "than"} ${maxValueStr}` | ||
message: `value must be less than${issue.inclusive ? " or equal to" : ""} ${maxValueStr}` | ||
}; | ||
@@ -460,3 +443,3 @@ } | ||
path: issue.path, | ||
message: `number must be greater ${issue.inclusive ? "or equal to" : "than"} ${minValueStr}` | ||
message: `number must be greater than${issue.inclusive ? " or equal to" : ""} ${minValueStr}` | ||
}; | ||
@@ -503,3 +486,3 @@ } | ||
path: issue.path, | ||
message: `value must be greater ${issue.inclusive ? "or equal to" : "than"} ${minValueStr}` | ||
message: `value must be greater than${issue.inclusive ? " or equal to" : ""} ${minValueStr}` | ||
}; | ||
@@ -524,22 +507,3 @@ } | ||
// lib/v4/errorMap/invalidKey.ts | ||
function parseInvalidKeyIssue(issue) { | ||
return { | ||
type: issue.code, | ||
path: issue.path, | ||
message: `unexpected key in ${issue.origin}` | ||
}; | ||
} | ||
// lib/v4/errorMap/custom.ts | ||
function parseCustomIssue(issue) { | ||
return { | ||
type: issue.code, | ||
path: issue.path, | ||
message: issue.message | ||
}; | ||
} | ||
// lib/v4/errorMap/errorMap.ts | ||
var BRAND = Symbol.for("zod-validation-error-map"); | ||
var issueParsers = { | ||
@@ -555,28 +519,6 @@ invalid_type: parseInvalidTypeIssue, | ||
invalid_key: parseInvalidKeyIssue, | ||
custom: parseCustomIssue, | ||
invalid_union: parseInvalidUnionIssue | ||
custom: parseInvalidUnionIssue, | ||
invalid_union: parseCustomIssue | ||
}; | ||
function parseInvalidUnionIssue(issue, options) { | ||
const errorMap = createErrorMap(options); | ||
const individualMessages = issue.errors.map( | ||
(issues) => issues.map( | ||
(subIssue) => errorMap({ | ||
...subIssue, | ||
path: issue.path.concat(subIssue.path) | ||
}) | ||
).join(options.issueSeparator) | ||
); | ||
const message = Array.from(new Set(individualMessages)).join( | ||
options.unionSeparator | ||
); | ||
return { | ||
type: issue.code, | ||
path: [], | ||
message | ||
}; | ||
} | ||
var defaultErrorMapOptions = { | ||
includePath: true, | ||
unionSeparator: " or ", | ||
issueSeparator: "; ", | ||
displayInvalidFormatDetails: false, | ||
@@ -591,15 +533,10 @@ allowedValuesSeparator: ", ", | ||
maxUnrecognizedKeysToDisplay: 5, | ||
issuesInTitleCase: true, | ||
dateLocalization: true, | ||
numberLocalization: true | ||
}; | ||
function equalsDefaultOptions(options) { | ||
for (const key in options) { | ||
if (options[key] !== defaultErrorMapOptions[key]) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
function makeErrorMap(options) { | ||
function createErrorMap(partialOptions = {}) { | ||
const options = { | ||
...defaultErrorMapOptions, | ||
...partialOptions | ||
}; | ||
const errorMap = (issue) => { | ||
@@ -611,51 +548,52 @@ if (issue.code === void 0) { | ||
const ast = parseFunc(issue, options); | ||
return toString(ast, options); | ||
return ast.message; | ||
}; | ||
Object.defineProperty(errorMap, "_brand", { | ||
value: BRAND, | ||
writable: false, | ||
enumerable: false, | ||
configurable: false | ||
}); | ||
return errorMap; | ||
} | ||
var defaultErrorMap = makeErrorMap(defaultErrorMapOptions); | ||
function createErrorMap(partialOptions = {}) { | ||
const options = { | ||
...defaultErrorMapOptions, | ||
...partialOptions | ||
}; | ||
if (equalsDefaultOptions(options)) { | ||
return defaultErrorMap; | ||
} | ||
return makeErrorMap(options); | ||
// lib/utils/NonEmptyArray.ts | ||
function isNonEmptyArray(value) { | ||
return value.length !== 0; | ||
} | ||
function toString(ast, options) { | ||
const buf = []; | ||
if (options.issuesInTitleCase) { | ||
buf.push(titleCase(ast.message)); | ||
} else { | ||
buf.push(ast.message); | ||
// lib/utils/joinPath.ts | ||
var identifierRegex = /[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*/u; | ||
function joinPath(path) { | ||
if (path.length === 1) { | ||
let propertyKey = path[0]; | ||
if (typeof propertyKey === "symbol") { | ||
propertyKey = stringifySymbol(propertyKey); | ||
} | ||
return propertyKey.toString() || '""'; | ||
} | ||
pathCondition: if (options.includePath && ast.path !== void 0 && isNonEmptyArray(ast.path)) { | ||
if (ast.path.length === 1) { | ||
const identifier = ast.path[0]; | ||
if (typeof identifier === "number") { | ||
buf.push(` at index ${identifier}`); | ||
break pathCondition; | ||
} | ||
return path.reduce((acc, propertyKey) => { | ||
if (typeof propertyKey === "number") { | ||
return acc + "[" + propertyKey.toString() + "]"; | ||
} | ||
buf.push(` at "${joinPath(ast.path)}"`); | ||
} | ||
return buf.join(""); | ||
if (typeof propertyKey === "symbol") { | ||
propertyKey = stringifySymbol(propertyKey); | ||
} | ||
if (propertyKey.includes('"')) { | ||
return acc + '["' + escapeQuotes(propertyKey) + '"]'; | ||
} | ||
if (!identifierRegex.test(propertyKey)) { | ||
return acc + '["' + propertyKey + '"]'; | ||
} | ||
const separator = acc.length === 0 ? "" : "."; | ||
return acc + separator + propertyKey; | ||
}, ""); | ||
} | ||
function isZodValidationErrorMap(errorMap) { | ||
return "_brand" in errorMap && errorMap._brand === BRAND; | ||
function escapeQuotes(str) { | ||
return str.replace(/"/g, '\\"'); | ||
} | ||
// lib/utils/titleCase.ts | ||
function titleCase(value) { | ||
if (value.length === 0) { | ||
return value; | ||
} | ||
return value.charAt(0).toUpperCase() + value.slice(1); | ||
} | ||
// lib/v4/MessageBuilder.ts | ||
var zod = __toESM(require("zod/v4/core")); | ||
var identityErrorMap = (issue) => { | ||
return issue.message; | ||
}; | ||
var defaultMessageBuilderOptions = { | ||
@@ -666,4 +604,6 @@ prefix: "Validation error", | ||
// I've got 99 problems but the b$tch ain't one | ||
issueSeparator: defaultErrorMapOptions.issueSeparator, | ||
error: defaultErrorMap | ||
unionSeparator: " or ", | ||
issueSeparator: "; ", | ||
includePath: true, | ||
forceTitleCase: true | ||
}; | ||
@@ -675,14 +615,40 @@ function createMessageBuilder(partialOptions = {}) { | ||
}; | ||
const errorMap = ( | ||
// user requested not to format errors by explicitly setting error to false | ||
options.error === false || // we have already formatted errors with zod-validation-error | ||
// using the zod.config() API | ||
// thus we should not format them again for performance reasons | ||
partialOptions.error === void 0 && zod.globalConfig.customError !== void 0 && isZodValidationErrorMap(zod.globalConfig.customError) ? identityErrorMap : options.error | ||
); | ||
return function messageBuilder(issues) { | ||
const message = issues.slice(0, options.maxIssuesInMessage).map(errorMap).join(options.issueSeparator); | ||
const message = issues.slice(0, options.maxIssuesInMessage).map((issue) => mapIssue(issue, options)).join(options.issueSeparator); | ||
return conditionallyPrefixMessage(message, options); | ||
}; | ||
} | ||
function mapIssue(issue, options) { | ||
if (issue.code === "invalid_union") { | ||
const individualMessages = issue.errors.map( | ||
(issues) => issues.map( | ||
(subIssue) => mapIssue( | ||
{ | ||
...subIssue, | ||
path: issue.path.concat(subIssue.path) | ||
}, | ||
options | ||
) | ||
).join(options.issueSeparator) | ||
); | ||
return Array.from(new Set(individualMessages)).join(options.unionSeparator); | ||
} | ||
const buf = []; | ||
if (options.forceTitleCase) { | ||
buf.push(titleCase(issue.message)); | ||
} else { | ||
buf.push(issue.message); | ||
} | ||
pathCondition: if (options.includePath && issue.path !== void 0 && isNonEmptyArray(issue.path)) { | ||
if (issue.path.length === 1) { | ||
const identifier = issue.path[0]; | ||
if (typeof identifier === "number") { | ||
buf.push(` at index ${identifier}`); | ||
break pathCondition; | ||
} | ||
} | ||
buf.push(` at "${joinPath(issue.path)}"`); | ||
} | ||
return buf.join(""); | ||
} | ||
function conditionallyPrefixMessage(message, options) { | ||
@@ -745,3 +711,3 @@ if (options.prefix != null) { | ||
// lib/v4/fromZodIssue.ts | ||
var zod2 = __toESM(require("zod/v4/core")); | ||
var zod = __toESM(require("zod/v4/core")); | ||
function fromZodIssue(issue, options = {}) { | ||
@@ -751,3 +717,3 @@ const messageBuilder = createMessageBuilderFromOptions2(options); | ||
return new ValidationError(message, { | ||
cause: new zod2.$ZodRealError([issue]) | ||
cause: new zod.$ZodRealError([issue]) | ||
}); | ||
@@ -754,0 +720,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
-100%1
-50%505
3.48%227954
-3.54%1981
-3.51%