push neon reactor

This commit is contained in:
2026-07-12 15:11:38 +02:00
parent 172e72dbcd
commit aeab5f7820
9597 changed files with 2407488 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+150
View File
@@ -0,0 +1,150 @@
# `eslint-plugin-react-hooks`
The official ESLint plugin for [React](https://react.dev) which enforces the [Rules of React](https://react.dev/reference/eslint-plugin-react-hooks) and other best practices.
## Installation
Assuming you already have ESLint installed, run:
```sh
# npm
npm install eslint-plugin-react-hooks --save-dev
# yarn
yarn add eslint-plugin-react-hooks --dev
```
### Flat Config (eslint.config.js|ts)
Add the `recommended` config for all recommended rules:
```js
// eslint.config.js
import reactHooks from 'eslint-plugin-react-hooks';
import { defineConfig } from 'eslint/config';
export default defineConfig([
reactHooks.configs.flat.recommended,
]);
```
If you want to try bleeding edge experimental compiler rules, use `recommended-latest`.
```js
// eslint.config.js
import reactHooks from 'eslint-plugin-react-hooks';
import { defineConfig } from 'eslint/config';
export default defineConfig([
reactHooks.configs.flat['recommended-latest'],
]);
```
### Legacy Config (.eslintrc)
If you are still using ESLint below 9.0.0, the `recommended` preset can also be used to enable all recommended rules.
```js
{
"extends": ["plugin:react-hooks/recommended"],
// ...
}
```
### Custom Configuration
If you want more fine-grained configuration, you can instead choose to enable specific rules. However, we strongly encourage using the recommended presets — see above — so that you will automatically receive new recommended rules as we add them in future versions of the plugin.
#### Flat Config (eslint.config.js|ts)
```js
import reactHooks from 'eslint-plugin-react-hooks';
export default [
{
files: ['**/*.{js,jsx}'],
plugins: { 'react-hooks': reactHooks },
// ...
rules: {
// Core hooks rules
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
// React Compiler rules
'react-hooks/config': 'error',
'react-hooks/error-boundaries': 'error',
'react-hooks/gating': 'error',
'react-hooks/globals': 'error',
'react-hooks/immutability': 'error',
'react-hooks/preserve-manual-memoization': 'error',
'react-hooks/purity': 'error',
'react-hooks/refs': 'error',
'react-hooks/set-state-in-effect': 'error',
'react-hooks/set-state-in-render': 'error',
'react-hooks/static-components': 'error',
'react-hooks/unsupported-syntax': 'warn',
'react-hooks/use-memo': 'error',
'react-hooks/incompatible-library': 'warn',
}
},
];
```
#### Legacy Config (.eslintrc)
```js
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
// Core hooks rules
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
// React Compiler rules
"react-hooks/config": "error",
"react-hooks/error-boundaries": "error",
"react-hooks/gating": "error",
"react-hooks/globals": "error",
"react-hooks/immutability": "error",
"react-hooks/preserve-manual-memoization": "error",
"react-hooks/purity": "error",
"react-hooks/refs": "error",
"react-hooks/set-state-in-effect": "error",
"react-hooks/set-state-in-render": "error",
"react-hooks/static-components": "error",
"react-hooks/unsupported-syntax": "warn",
"react-hooks/use-memo": "error",
"react-hooks/incompatible-library": "warn"
}
}
```
## Advanced Configuration
`exhaustive-deps` can be configured to validate dependencies of custom Hooks with the `additionalHooks` option.
This option accepts a regex to match the names of custom Hooks that have dependencies.
```js
{
rules: {
// ...
"react-hooks/exhaustive-deps": ["warn", {
additionalHooks: "(useMyCustomHook|useMyOtherCustomHook)"
}]
}
}
```
We suggest to use this option **very sparingly, if at all**. Generally saying, we recommend most custom Hooks to not use the dependencies argument, and instead provide a higher-level API that is more focused around a specific use case.
## Valid and Invalid Examples
Please refer to the [Rules of Hooks](https://react.dev/reference/rules/rules-of-hooks) documentation to learn more about this rule.
## License
MIT
@@ -0,0 +1,101 @@
import * as estree from 'estree';
import { Rule, Linter } from 'eslint';
type ReactHooksFlatConfig = {
plugins: {
react: any;
};
rules: Linter.RulesRecord;
};
declare const plugin: {
meta: {
name: string;
version: string;
};
rules: {
'component-hook-factories': Rule.RuleModule;
'exhaustive-deps': {
meta: {
type: "suggestion";
docs: {
description: string;
recommended: true;
url: string;
};
fixable: "code";
hasSuggestions: true;
schema: {
type: "object";
additionalProperties: false;
enableDangerousAutofixThisMayCauseInfiniteLoops: boolean;
properties: {
additionalHooks: {
type: "string";
};
enableDangerousAutofixThisMayCauseInfiniteLoops: {
type: "boolean";
};
experimental_autoDependenciesHooks: {
type: "array";
items: {
type: "string";
};
};
requireExplicitEffectDeps: {
type: "boolean";
};
};
}[];
};
create(context: Rule.RuleContext): {
CallExpression: (node: estree.CallExpression) => void;
};
};
'rules-of-hooks': {
meta: {
type: "problem";
docs: {
description: string;
recommended: true;
url: string;
};
schema: {
type: "object";
additionalProperties: false;
properties: {
additionalHooks: {
type: "string";
};
};
}[];
};
create(context: Rule.RuleContext): {
'*'(node: any): void;
'*:exit'(node: any): void;
CallExpression(node: estree.CallExpression & Rule.NodeParentExtension): void;
Identifier(node: estree.Identifier & Rule.NodeParentExtension): void;
'CallExpression:exit'(node: estree.CallExpression & Rule.NodeParentExtension): void;
FunctionDeclaration(node: estree.FunctionDeclaration & Rule.NodeParentExtension): void;
ArrowFunctionExpression(node: estree.ArrowFunctionExpression & Rule.NodeParentExtension): void;
ComponentDeclaration(node: any): void;
HookDeclaration(node: any): void;
};
};
};
configs: {
recommended: {
plugins: string[];
rules: Linter.RulesRecord;
};
'recommended-latest': {
plugins: string[];
rules: Linter.RulesRecord;
};
flat: {
recommended: ReactHooksFlatConfig;
"recommended-latest": ReactHooksFlatConfig;
};
};
};
export { plugin as default };
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import reactHooks from './cjs/eslint-plugin-react-hooks';
export = reactHooks;
+26
View File
@@ -0,0 +1,26 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
// TODO: this doesn't make sense for an ESLint rule.
// We need to fix our build process to not create bundles for "raw" packages like this.
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/eslint-plugin-react-hooks.production.js');
} else {
module.exports = require('./cjs/eslint-plugin-react-hooks.development.js');
}
// Hint to Nodes cjs-module-lexer to make named imports work
// https://github.com/facebook/react/issues/34801#issuecomment-3433478810
// eslint-disable-next-line ft-flow/no-unused-expressions
0 &&
(module.exports = {
meta: true,
rules: true,
configs: true,
});
@@ -0,0 +1,9 @@
(The MIT License)
Copyright 2022 Causaly, Inc <front-end@causaly.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,504 @@
# zod-validation-error
Wrap zod validation errors in user-friendly readable messages.
[![Build Status](https://github.com/causaly/zod-validation-error/actions/workflows/ci.yml/badge.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)
#### Features
- User-friendly readable error messages with extensive configuration options;
- Preserves original error details accessible via `error.details`;
- 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 refer to the [v3 documentation](./README.v3.md)
## Installation
```bash
npm install zod-validation-error
```
#### Requirements
- Node.js v.18+
- TypeScript v.4.5+
## Quick start
```typescript
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;
}
```
## Motivation
Zod errors are difficult to consume for the end-user. This library wraps Zod validation errors in user-friendly readable messages that can be exposed to the outer world, while maintaining the original errors in an array for _dev_ use.
### Example
#### Input (from Zod)
```json
[
{
"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\""
}
]
```
#### Output
```
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
Main `ValidationError` class, extending JavaScript's native `Error`.
#### Arguments
- `message` - _string_; error message (required)
- `options` - _ErrorOptions_; error options as per [JavaScript definition](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Error#options) (optional)
- `options.cause` - _any_; can be used to hold the original zod error (optional)
#### Example 1: construct new ValidationError with `message`
```typescript
import { ValidationError } from 'zod-validation-error';
const error = new ValidationError('foobar');
console.log(error instanceof Error); // prints true
```
#### Example 2: construct new ValidationError with `message` and `options.cause`
```typescript
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
Creates zod-validation-error's `errorMap`, which is used to format issues into user-friendly error messages.
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), e.g.
#### Arguments
- `options` - _Object_; formatting options (optional)
##### createErrorMap Options
| 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. |
#### Example
```typescript
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
Creates zod-validation-error's default `MessageBuilder`, which is used to produce user-friendly error messages.
Meant to be passed as an option to [fromError](#fromerror), [fromZodIssue](#fromzodissue), [fromZodError](#fromzoderror) or [toValidationError](#tovalidationerror).
#### Arguments
- `options` - _Object_; formatting options (optional)
##### createMessageBuilder Options
| 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`). |
#### Example
```typescript
import { createMessageBuilder } from 'zod-validation-error';
const messageBuilder = createMessageBuilder({
maxIssuesInMessage: 3,
includePath: false,
});
```
### isValidationError
A [type guard](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) utility function, based on `instanceof` comparison.
#### Arguments
- `error` - error instance (required)
#### Example
```typescript
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
A [type guard](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) utility function, based on _heuristics_ comparison.
_Why do we need heuristics since we can use a simple `instanceof` comparison?_ Because of multi-version inconsistencies. For instance, it's possible that a dependency is using an older `zod-validation-error` version internally. In such case, the `instanceof` comparison will yield invalid results because module deduplication does not apply at npm/yarn level and the prototype is different.
tl;dr if you are uncertain then it is preferable to use `isValidationErrorLike` instead of `isValidationError`.
#### Arguments
- `error` - error instance (required)
#### Example
```typescript
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
A [type guard](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) utility function, based on _heuristics_ comparison.
_Why do we need heuristics since we can use a simple `instanceof` comparison?_ Because of multi-version inconsistencies. For instance, it's possible that a dependency is using an older `zod` version internally. In such case, the `instanceof` comparison will yield invalid results because module deduplication does not apply at npm/yarn level and the prototype is different.
#### Arguments
- `error` - error instance (required)
#### Example
```typescript
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
Converts an error to `ValidationError`.
_What is the difference between `fromError` and `fromZodError`?_ The `fromError` function is a less strict version of `fromZodError`. It can accept an unknown error and attempt to convert it to a `ValidationError`.
#### Arguments
- `error` - _unknown_; an error (required)
- `options` - _Object_; formatting options (optional)
- `messageBuilder` - _MessageBuilder_; a function that accepts an array of `zod.ZodIssue` objects and returns a user-friendly error message in the form of a `string` (optional).
#### Notes
Alternatively, you may pass [createMessageBuilder options](#createmessagebuilder-options) directly as `options`. These will be used as arguments to create the `MessageBuilder` instance internally.
### fromZodIssue
Converts a single zod issue to `ValidationError`.
#### Arguments
- `zodIssue` - _zod.ZodIssue_; a ZodIssue instance (required)
- `options` - _Object_; formatting options (optional)
- `messageBuilder` - _MessageBuilder_; a function that accepts an array of `zod.ZodIssue` objects and returns a user-friendly error message in the form of a `string` (optional).
#### Notes
Alternatively, you may pass [createMessageBuilder options](#createmessagebuilder-options) directly as `options`. These will be used as arguments to create the `MessageBuilder` instance internally.
### fromZodError
Converts zod error to `ValidationError`.
_Why is the difference between `ZodError` and `ZodIssue`?_ A `ZodError` is a collection of 1 or more `ZodIssue` instances. It's what you get when you call `zodSchema.parse()`.
#### Arguments
- `zodError` - _zod.ZodError_; a ZodError instance (required)
- `options` - _Object_; formatting options (optional)
- `messageBuilder` - _MessageBuilder_; a function that accepts an array of `zod.ZodIssue` objects and returns a user-friendly error message in the form of a `string` (optional).
#### Notes
Alternatively, you may pass [createMessageBuilder options](#createmessagebuilder-optionscreateMessageBuilder) directly as `options`. These will be used as arguments to create the `MessageBuilder` instance internally.
### toValidationError
A curried version of `fromZodError` meant to be used for FP (Functional Programming). Note it first takes the options object if needed and returns a function that converts the `zodError` to a `ValidationError` object
```js
toValidationError(options) => (zodError) => ValidationError
```
#### Example using fp-ts
```typescript
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<typeof zodSchema>;
export function parse(
value: zod.input<typeof zodSchema>
): Either.Either<ValidationError, User> {
return Either.tryCatch(() => schema.parse(value), toValidationError());
}
```
## FAQ
### What is the difference between zod-validation-error and zod's own [prettifyError](https://zod.dev/error-formatting#zprettifyerror)?
While both libraries aim to provide a human-readable string representation of a zod error, they differ in several ways...
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.
1. **Error handling**: zod-validation-error maintains the original error details while providing a clean, consistent interface through the ValidationError class.
1. **Integration flexibility**: Beyond just formatting, zod-validation-error provides utility functions for error detection and conversion that work well in various architectural patterns, e.g. functional programming.
Disclaimer: as per this [comment](https://github.com/causaly/zod-validation-error/issues/455#issuecomment-2811895152), we have no intention to antagonize zod. In fact, we are happy to decommission this module assuming it's in the best interest of the community. As of now, it seems that there's room for both `zod-validation-error` and `prettifyError`, also based on Colin McDonnell's [response](https://github.com/causaly/zod-validation-error/issues/455#issuecomment-2814466019).
### 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
Use the `isValidationErrorLike` type guard.
#### Example
Scenario: Distinguish between `ValidationError` VS generic `Error` in order to respond with 400 VS 500 HTTP status code respectively.
```typescript
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
}
```
### How to use `ValidationError` outside `zod`
It's possible to implement custom validation logic outside `zod` and throw a `ValidationError`.
#### Example 1: passing custom message
```typescript
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;
}
```
#### Example 2: passing custom message and original error as cause
```typescript
import { ValidationError } from 'zod-validation-error';
try {
// do something that throws an error
} catch (err) {
throw new ValidationError('Something went deeply wrong', { cause: err });
}
```
### How to use `ValidationError` with custom "error map"
Zod supports customizing error messages by providing a custom "error map". You may combine this with `zod-validation-error` to produce user-friendly messages.
#### Example: produce user-friendly error messages using the `customError` property
If all you need is to produce user-friendly error messages you may use the `customError` property.
```typescript
import { z as zod } from 'zod';
import { createErrorMap } from 'zod-validation-error';
zod.config({
customError: createErrorMap({
includePath: true,
}),
});
```
`zod-validation-error` will respect the `customError` property when it is set, no further configuration is needed.
### Does `zod-validation-error` support CommonJS
Yes, `zod-validation-error` supports CommonJS out-of-the-box. All you need to do is import it using `require`.
#### Example
```typescript
const { ValidationError } = require('zod-validation-error');
```
## Contribute
Source code contributions are most welcome. Please open a PR, ensure the linter is satisfied and all tests pass.
#### We are hiring
Causaly is building the world's largest biomedical knowledge platform, using technologies such as TypeScript, React and Node.js. Find out more about our openings at https://jobs.ashbyhq.com/causaly.
## License
MIT
@@ -0,0 +1,558 @@
# zod-validation-error
Wrap zod validation errors in user-friendly readable messages.
[![Build Status](https://github.com/causaly/zod-validation-error/actions/workflows/ci.yml/badge.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)
#### Features
- User-friendly readable messages, configurable via options;
- Maintain original issues under `error.details`;
- Supports both `zod` v3 and v4.
**_Note:_** This is the v3 version of `zod-validation-error`. If you are looking for zod v4 support, please click [here](/README.md).
## Installation
```bash
npm install zod-validation-error
```
#### Requirements
- Node.js v.18+
- TypeScript v.4.5+
## Quick start
```typescript
import { z as zod } from 'zod/v3';
import { fromError } from 'zod-validation-error/v3';
// create zod schema
const zodSchema = zod.object({
id: zod.number().int().positive(),
email: zod.string().email(),
});
// parse some invalid value
try {
zodSchema.parse({
id: 1,
email: 'foobar', // 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;
}
```
## Motivation
Zod errors are difficult to consume for the end-user. This library wraps Zod validation errors in user-friendly readable messages that can be exposed to the outer world, while maintaining the original errors in an array for _dev_ use.
### Example
#### Input (from Zod)
```json
[
{
"code": "too_small",
"inclusive": false,
"message": "Number must be greater than 0",
"minimum": 0,
"path": ["id"],
"type": "number"
},
{
"code": "invalid_string",
"message": "Invalid email",
"path": ["email"],
"validation": "email"
}
]
```
#### Output
```
Validation error: Number must be greater than 0 at "id"; Invalid email at "email"
```
## API
- [ValidationError(message[, options])](#validationerror)
- [createMessageBuilder(props)](#createMessageBuilder)
- [errorMap](#errormap)
- [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
Main `ValidationError` class, extending native JavaScript `Error`.
#### Arguments
- `message` - _string_; error message (required)
- `options` - _ErrorOptions_; error options as per [JavaScript definition](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Error#options) (optional)
- `options.cause` - _any_; can be used to hold the original zod error (optional)
#### Example 1: construct new ValidationError with `message`
```typescript
const { ValidationError } = require('zod-validation-error');
const error = new ValidationError('foobar');
console.log(error instanceof Error); // prints true
```
#### Example 2: construct new ValidationError with `message` and `options.cause`
```typescript
import { z as zod } from 'zod/v3';
const { ValidationError } = require('zod-validation-error');
const error = new ValidationError('foobar', {
cause: new zod.ZodError([
{
code: 'invalid_string',
message: 'Invalid email',
path: ['email'],
validation: 'email',
},
]),
});
console.log(error.details); // prints issues from zod error
```
### createMessageBuilder
Creates zod-validation-error's default `MessageBuilder`, which is used to produce user-friendly error messages.
Meant to be passed as an option to [fromError](#fromerror), [fromZodIssue](#fromzodissue), [fromZodError](#fromzoderror) or [toValidationError](#tovalidationerror).
You may read more on the concept of the `MessageBuilder` further [below](#MessageBuilder).
#### Arguments
- `props` - _Object_; formatting options (optional)
- `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_ or _null_; prefix to use in user-friendly message (optional, defaults to "Validation error"). Pass `null` 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 `null`.
- `includePath` - _boolean_; used to provide control on whether to include the erroneous property name suffix or not (optional, defaults to `true`).
#### Example
```typescript
import { createMessageBuilder } from 'zod-validation-error/v3';
const messageBuilder = createMessageBuilder({
includePath: false,
maxIssuesInMessage: 3,
});
```
### errorMap
A custom error map to use with zod's `setErrorMap` method and get user-friendly messages automatically.
#### Example
```typescript
import { z as zod } from 'zod/v3';
import { errorMap } from 'zod-validation-error/v3';
zod.setErrorMap(errorMap);
```
### isValidationError
A [type guard](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) utility function, based on `instanceof` comparison.
#### Arguments
- `error` - error instance (required)
#### Example
```typescript
import { z as zod } from 'zod/v3';
import { ValidationError, isValidationError } from 'zod-validation-error/v3';
const err = new ValidationError('foobar');
isValidationError(err); // returns true
const invalidErr = new Error('foobar');
isValidationError(err); // returns false
```
### isValidationErrorLike
A [type guard](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) utility function, based on _heuristics_ comparison.
_Why do we need heuristics since we can use a simple `instanceof` comparison?_ Because of multi-version inconsistencies. For instance, it's possible that a dependency is using an older `zod-validation-error` version internally. In such case, the `instanceof` comparison will yield invalid results because module deduplication does not apply at npm/yarn level and the prototype is different.
tl;dr if you are uncertain then it is preferable to use `isValidationErrorLike` instead of `isValidationError`.
#### Arguments
- `error` - error instance (required)
#### Example
```typescript
import {
ValidationError,
isValidationErrorLike,
} from 'zod-validation-error/v3';
const err = new ValidationError('foobar');
isValidationErrorLike(err); // returns true
const invalidErr = new Error('foobar');
isValidationErrorLike(err); // returns false
```
### isZodErrorLike
A [type guard](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) utility function, based on _heuristics_ comparison.
_Why do we need heuristics since we can use a simple `instanceof` comparison?_ Because of multi-version inconsistencies. For instance, it's possible that a dependency is using an older `zod` version internally. In such case, the `instanceof` comparison will yield invalid results because module deduplication does not apply at npm/yarn level and the prototype is different.
#### Arguments
- `error` - error instance (required)
#### Example
```typescript
import { z as zod } from 'zod/v3';
import { ValidationError, isZodErrorLike } from 'zod-validation-error/v3';
const zodValidationErr = new ValidationError('foobar');
isZodErrorLike(zodValidationErr); // returns false
const genericErr = new Error('foobar');
isZodErrorLike(genericErr); // returns false
const zodErr = new zod.ZodError([
{
code: zod.ZodIssueCode.custom,
path: [],
message: 'foobar',
fatal: true,
},
]);
isZodErrorLike(zodErr); // returns true
```
### fromError
Converts an error to `ValidationError`.
_What is the difference between `fromError` and `fromZodError`?_ The `fromError` function is a less strict version of `fromZodError`. It can accept an unknown error and attempt to convert it to a `ValidationError`.
#### Arguments
- `error` - _unknown_; an error (required)
- `options` - _Object_; formatting options (optional)
- `messageBuilder` - _MessageBuilder_; a function that accepts an array of `zod.ZodIssue` objects and returns a user-friendly error message in the form of a `string` (optional).
#### Notes
Alternatively, you may pass the following `options` instead of a `messageBuilder`.
- `options` - _Object_; formatting options (optional)
- `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_ or _null_; prefix to use in user-friendly message (optional, defaults to "Validation error"). Pass `null` 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 `null`.
- `includePath` - _boolean_; used to provide control on whether to include the erroneous property name suffix or not (optional, defaults to `true`).
They will be passed as arguments to the [createMessageBuilder](#createMessageBuilder) function. The only reason they exist is to provide backwards-compatibility with older versions of `zod-validation-error`. They should however be considered deprecated and may be removed in the future.
### fromZodIssue
Converts a single zod issue to `ValidationError`.
#### Arguments
- `zodIssue` - _zod.ZodIssue_; a ZodIssue instance (required)
- `options` - _Object_; formatting options (optional)
- `messageBuilder` - _MessageBuilder_; a function that accepts an array of `zod.ZodIssue` objects and returns a user-friendly error message in the form of a `string` (optional).
#### Notes
Alternatively, you may pass the following `options` instead of a `messageBuilder`.
- `options` - _Object_; formatting options (optional)
- `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_ or _null_; prefix to use in user-friendly message (optional, defaults to "Validation error"). Pass `null` 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 `null`.
- `includePath` - _boolean_; used to provide control on whether to include the erroneous property name suffix or not (optional, defaults to `true`).
They will be passed as arguments to the [createMessageBuilder](#createMessageBuilder) function. The only reason they exist is to provide backwards-compatibility with older versions of `zod-validation-error`. They should however be considered deprecated and may be removed in the future.
### fromZodError
Converts zod error to `ValidationError`.
_Why is the difference between `ZodError` and `ZodIssue`?_ A `ZodError` is a collection of 1 or more `ZodIssue` instances. It's what you get when you call `zodSchema.parse()`.
#### Arguments
- `zodError` - _zod.ZodError_; a ZodError instance (required)
- `options` - _Object_; formatting options (optional)
- `messageBuilder` - _MessageBuilder_; a function that accepts an array of `zod.ZodIssue` objects and returns a user-friendly error message in the form of a `string` (optional).
#### Notes
Alternatively, you may pass the following `options` instead of a `messageBuilder`.
- `options` - _Object_; formatting options (optional)
- `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_ or _null_; prefix to use in user-friendly message (optional, defaults to "Validation error"). Pass `null` 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 `null`.
- `includePath` - _boolean_; used to provide control on whether to include the erroneous property name suffix or not (optional, defaults to `true`).
They will be passed as arguments to the [createMessageBuilder](#createMessageBuilder) function. The only reason they exist is to provide backwards-compatibility with older versions of `zod-validation-error`. They should however be considered deprecated and may be removed in the future.
### toValidationError
A curried version of `fromZodError` meant to be used for FP (Functional Programming). Note it first takes the options object if needed and returns a function that converts the `zodError` to a `ValidationError` object
```js
toValidationError(options) => (zodError) => ValidationError
```
#### Example using fp-ts
```typescript
import * as Either from 'fp-ts/Either';
import { z as zod } from 'zod/v3';
import { toValidationError, ValidationError } from 'zod-validation-error/v3';
// create zod schema
const zodSchema = zod
.object({
id: zod.number().int().positive(),
email: zod.string().email(),
})
.brand<'User'>();
export type User = zod.infer<typeof zodSchema>;
export function parse(
value: zod.input<typeof zodSchema>
): Either.Either<ValidationError, User> {
return Either.tryCatch(() => schema.parse(value), toValidationError());
}
```
## MessageBuilder
`zod-validation-error` can be configured with a custom `MessageBuilder` function in order to produce case-specific error messages.
#### Example
For instance, one may want to print `invalid_string` errors to the console in red color.
```typescript
import { z as zod } from 'zod/v3';
import { type MessageBuilder, fromError } from 'zod-validation-error/v3';
import chalk from 'chalk';
// create custom MessageBuilder
const myMessageBuilder: MessageBuilder = (issues) => {
return (
issues
// format error message
.map((issue) => {
if (issue.code === zod.ZodIssueCode.invalid_string) {
return chalk.red(issue.message);
}
return issue.message;
})
// join as string with new-line character
.join('\n')
);
};
// create zod schema
const zodSchema = zod.object({
id: zod.number().int().positive(),
email: zod.string().email(),
});
// parse some invalid value
try {
zodSchema.parse({
id: 1,
email: 'foobar', // note: invalid email value
});
} catch (err) {
const validationError = fromError(err, {
messageBuilder: myMessageBuilder,
});
// the error is now displayed with red letters
console.log(validationError.toString());
}
```
## FAQ
### How to distinguish between errors
Use the `isValidationErrorLike` type guard.
#### Example
Scenario: Distinguish between `ValidationError` VS generic `Error` in order to respond with 400 VS 500 HTTP status code respectively.
```typescript
import * as Either from 'fp-ts/Either';
import { z as zod } from 'zod/v3';
import { isValidationErrorLike } from 'zod-validation-error/v3';
try {
func(); // throws Error - or - ValidationError
} catch (err) {
if (isValidationErrorLike(err)) {
return 400; // Bad Data (this is a client error)
}
return 500; // Server Error
}
```
### How to use `ValidationError` outside `zod`
It's possible to implement custom validation logic outside `zod` and throw a `ValidationError`.
#### Example 1: passing custom message
```typescript
import { ValidationError } from 'zod-validation-error/v3';
import { Buffer } from 'node:buffer';
function parseBuffer(buf: unknown): Buffer {
if (!Buffer.isBuffer(buf)) {
throw new ValidationError('Invalid argument; expected buffer');
}
return buf;
}
```
#### Example 2: passing custom message and original error as cause
```typescript
import { ValidationError } from 'zod-validation-error/v3';
try {
// do something that throws an error
} catch (err) {
throw new ValidationError('Something went deeply wrong', { cause: err });
}
```
### How to use `ValidationError` with custom "error map"
Zod supports customizing error messages by providing a custom "error map". You may combine this with `zod-validation-error` to produce user-friendly messages.
#### Example 1: produce user-friendly error messages using the `errorMap` property
If all you need is to produce user-friendly error messages you may use the `errorMap` property.
```typescript
import { z as zod } from 'zod/v3';
import { errorMap } from 'zod-validation-error/v3';
zod.setErrorMap(errorMap);
```
#### Example 2: extra customization using `fromZodIssue`
If you need to customize some error code, you may use the `fromZodIssue` function.
```typescript
import { z as zod } from 'zod/v3';
import { fromZodIssue } from 'zod-validation-error/v3';
const customErrorMap: zod.ZodErrorMap = (issue, ctx) => {
switch (issue.code) {
case ZodIssueCode.invalid_type: {
return {
message:
'Custom error message of your preference for invalid_type errors',
};
}
default: {
const validationError = fromZodIssue({
...issue,
// fallback to the default error message
// when issue does not have a message
message: issue.message ?? ctx.defaultError,
});
return {
message: validationError.message,
};
}
}
};
zod.setErrorMap(customErrorMap);
```
### How to use `zod-validation-error` with `react-hook-form`?
```typescript
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { errorMap } from 'zod-validation-error/v3';
useForm({
resolver: zodResolver(schema, { errorMap }),
});
```
### Does `zod-validation-error` support CommonJS
Yes, `zod-validation-error` supports CommonJS out-of-the-box. All you need to do is import it using `require`.
#### Example
```typescript
const { ValidationError } = require('zod-validation-error');
```
## Contribute
Source code contributions are most welcome. Please open a PR, ensure the linter is satisfied and all tests pass.
#### We are hiring
Causaly is building the world's largest biomedical knowledge platform, using technologies such as TypeScript, React and Node.js. Find out more about our openings at https://jobs.ashbyhq.com/causaly.
## License
MIT
@@ -0,0 +1,108 @@
{
"name": "zod-validation-error",
"version": "4.0.2",
"description": "Wrap zod validation errors in user-friendly readable messages",
"keywords": [
"zod",
"error",
"validation"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/causaly/zod-validation-error.git"
},
"author": {
"name": "Dimitrios C. Michalakos",
"email": "dimitris@jmike.gr",
"url": "https://github.com/jmike"
},
"contributors": [
{
"name": "Thanos Karagiannis",
"email": "hey@maestros.io",
"url": "https://github.com/thanoskrg"
},
{
"name": "Nikos Tsompanides",
"email": "nikostsompanides@gmail.com",
"url": "https://github.com/NikosTsompanides"
},
{
"name": "Nikos Kalogridis",
"url": "https://github.com/nikoskalogridis"
}
],
"main": "./v4/index.js",
"module": "./v4/index.mjs",
"types": "./v4/index.d.ts",
"exports": {
".": {
"types": "./v4/index.d.ts",
"require": "./v4/index.js",
"import": "./v4/index.mjs"
},
"./v3": {
"types": "./v3/index.d.ts",
"require": "./v3/index.js",
"import": "./v3/index.mjs"
},
"./v4": {
"types": "./v4/index.d.ts",
"require": "./v4/index.js",
"import": "./v4/index.mjs"
}
},
"files": [
"v3",
"v4"
],
"publishConfig": {
"access": "public"
},
"sideEffects": false,
"engines": {
"node": ">=18.0.0"
},
"scripts": {
"typecheck": "tsc --noEmit",
"build": "tsup --config ./tsup.config.ts",
"lint": "eslint lib --ext .ts",
"format": "prettier --config ./.prettierrc --ignore-path .gitignore -w .",
"test": "vitest run",
"changeset": "changeset",
"prerelease": "npm run build && npm run test",
"release": "changeset publish",
"prepare": "husky"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --config ./.prettierrc.json --write"
]
},
"devDependencies": {
"@changesets/changelog-github": "^0.5.0",
"@changesets/cli": "^2.27.7",
"@commitlint/cli": "^18.0.0",
"@commitlint/config-conventional": "^18.0.0",
"@types/node": "^20.5.0",
"@typescript-eslint/eslint-plugin": "^6.4.1",
"@typescript-eslint/parser": "^6.4.1",
"concurrently": "^8.2.0",
"eslint": "^8.4.1",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^4.2.1",
"husky": "^9.1.7",
"lint-staged": "^15.0.1",
"prettier": "^2.8.8",
"tsup": "^8.0.2",
"typescript": "^5.1.6",
"vitest": "^3.1.2",
"zod": "^4.0.2"
},
"peerDependencies": {
"zod": "^3.25.0 || ^4.0.0"
}
}
@@ -0,0 +1,50 @@
import * as zod from 'zod/v3';
interface ErrorOptions {
cause?: unknown;
}
declare class ValidationError extends Error {
name: 'ZodValidationError';
details: Array<zod.ZodIssue>;
constructor(message?: string, options?: ErrorOptions);
toString(): string;
}
declare function isValidationError(err: unknown): err is ValidationError;
declare function isValidationErrorLike(err: unknown): err is ValidationError;
declare function isZodErrorLike(err: unknown): err is zod.ZodError;
declare const errorMap: zod.ZodErrorMap;
type NonEmptyArray<T> = [T, ...T[]];
type ZodIssue = zod.ZodIssue;
type MessageBuilder = (issues: NonEmptyArray<ZodIssue>) => string;
type CreateMessageBuilderProps = {
issueSeparator?: string;
unionSeparator?: string;
prefix?: string | null;
prefixSeparator?: string;
includePath?: boolean;
maxIssuesInMessage?: number;
};
declare function createMessageBuilder(props?: CreateMessageBuilderProps): MessageBuilder;
type ZodError = zod.ZodError;
type FromZodErrorOptions = {
messageBuilder: MessageBuilder;
} | CreateMessageBuilderProps;
declare function fromZodError(zodError: ZodError, options?: FromZodErrorOptions): ValidationError;
declare function fromError(err: unknown, options?: FromZodErrorOptions): ValidationError;
type FromZodIssueOptions = {
messageBuilder: MessageBuilder;
} | Omit<CreateMessageBuilderProps, 'maxIssuesInMessage'>;
declare function fromZodIssue(issue: ZodIssue, options?: FromZodIssueOptions): ValidationError;
declare const toValidationError: (options?: FromZodErrorOptions) => (err: unknown) => ValidationError;
export { type ErrorOptions, type FromZodErrorOptions, type FromZodIssueOptions, type MessageBuilder, type NonEmptyArray, ValidationError, type ZodError, type ZodIssue, createMessageBuilder, errorMap, fromError, fromZodError, fromZodIssue, isValidationError, isValidationErrorLike, isZodErrorLike, toValidationError };
@@ -0,0 +1,50 @@
import * as zod from 'zod/v3';
interface ErrorOptions {
cause?: unknown;
}
declare class ValidationError extends Error {
name: 'ZodValidationError';
details: Array<zod.ZodIssue>;
constructor(message?: string, options?: ErrorOptions);
toString(): string;
}
declare function isValidationError(err: unknown): err is ValidationError;
declare function isValidationErrorLike(err: unknown): err is ValidationError;
declare function isZodErrorLike(err: unknown): err is zod.ZodError;
declare const errorMap: zod.ZodErrorMap;
type NonEmptyArray<T> = [T, ...T[]];
type ZodIssue = zod.ZodIssue;
type MessageBuilder = (issues: NonEmptyArray<ZodIssue>) => string;
type CreateMessageBuilderProps = {
issueSeparator?: string;
unionSeparator?: string;
prefix?: string | null;
prefixSeparator?: string;
includePath?: boolean;
maxIssuesInMessage?: number;
};
declare function createMessageBuilder(props?: CreateMessageBuilderProps): MessageBuilder;
type ZodError = zod.ZodError;
type FromZodErrorOptions = {
messageBuilder: MessageBuilder;
} | CreateMessageBuilderProps;
declare function fromZodError(zodError: ZodError, options?: FromZodErrorOptions): ValidationError;
declare function fromError(err: unknown, options?: FromZodErrorOptions): ValidationError;
type FromZodIssueOptions = {
messageBuilder: MessageBuilder;
} | Omit<CreateMessageBuilderProps, 'maxIssuesInMessage'>;
declare function fromZodIssue(issue: ZodIssue, options?: FromZodIssueOptions): ValidationError;
declare const toValidationError: (options?: FromZodErrorOptions) => (err: unknown) => ValidationError;
export { type ErrorOptions, type FromZodErrorOptions, type FromZodIssueOptions, type MessageBuilder, type NonEmptyArray, ValidationError, type ZodError, type ZodIssue, createMessageBuilder, errorMap, fromError, fromZodError, fromZodIssue, isValidationError, isValidationErrorLike, isZodErrorLike, toValidationError };
@@ -0,0 +1,309 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// lib/v3/index.ts
var index_exports = {};
__export(index_exports, {
ValidationError: () => ValidationError,
createMessageBuilder: () => createMessageBuilder,
errorMap: () => errorMap,
fromError: () => fromError,
fromZodError: () => fromZodError,
fromZodIssue: () => fromZodIssue,
isValidationError: () => isValidationError,
isValidationErrorLike: () => isValidationErrorLike,
isZodErrorLike: () => isZodErrorLike,
toValidationError: () => toValidationError
});
module.exports = __toCommonJS(index_exports);
// lib/v3/isZodErrorLike.ts
function isZodErrorLike(err) {
return err instanceof Error && err.name === "ZodError" && "issues" in err && Array.isArray(err.issues);
}
// lib/v3/ValidationError.ts
var ValidationError = class extends Error {
name;
details;
constructor(message, options) {
super(message, options);
this.name = "ZodValidationError";
this.details = getIssuesFromErrorOptions(options);
}
toString() {
return this.message;
}
};
function getIssuesFromErrorOptions(options) {
if (options) {
const cause = options.cause;
if (isZodErrorLike(cause)) {
return cause.issues;
}
}
return [];
}
// lib/v3/isValidationError.ts
function isValidationError(err) {
return err instanceof ValidationError;
}
// lib/v3/isValidationErrorLike.ts
function isValidationErrorLike(err) {
return err instanceof Error && err.name === "ZodValidationError";
}
// lib/v3/fromZodIssue.ts
var zod2 = __toESM(require("zod/v3"));
// lib/v3/MessageBuilder.ts
var zod = __toESM(require("zod/v3"));
// lib/utils/NonEmptyArray.ts
function isNonEmptyArray(value) {
return value.length !== 0;
}
// lib/utils/stringify.ts
function stringifySymbol(symbol) {
return symbol.description ?? "";
}
// 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/v3/config.ts
var ISSUE_SEPARATOR = "; ";
var MAX_ISSUES_IN_MESSAGE = 99;
var PREFIX = "Validation error";
var PREFIX_SEPARATOR = ": ";
var UNION_SEPARATOR = ", or ";
// lib/v3/MessageBuilder.ts
function createMessageBuilder(props = {}) {
const {
issueSeparator = ISSUE_SEPARATOR,
unionSeparator = UNION_SEPARATOR,
prefixSeparator = PREFIX_SEPARATOR,
prefix = PREFIX,
includePath = true,
maxIssuesInMessage = MAX_ISSUES_IN_MESSAGE
} = props;
return (issues) => {
const message = issues.slice(0, maxIssuesInMessage).map(
(issue) => getMessageFromZodIssue({
issue,
issueSeparator,
unionSeparator,
includePath
})
).join(issueSeparator);
return prefixMessage(message, prefix, prefixSeparator);
};
}
function getMessageFromZodIssue(props) {
const { issue, issueSeparator, unionSeparator, includePath } = props;
if (issue.code === zod.ZodIssueCode.invalid_union) {
return issue.unionErrors.reduce((acc, zodError) => {
const newIssues = zodError.issues.map(
(issue2) => getMessageFromZodIssue({
issue: issue2,
issueSeparator,
unionSeparator,
includePath
})
).join(issueSeparator);
if (!acc.includes(newIssues)) {
acc.push(newIssues);
}
return acc;
}, []).join(unionSeparator);
}
if (issue.code === zod.ZodIssueCode.invalid_arguments) {
return [
issue.message,
...issue.argumentsError.issues.map(
(issue2) => getMessageFromZodIssue({
issue: issue2,
issueSeparator,
unionSeparator,
includePath
})
)
].join(issueSeparator);
}
if (issue.code === zod.ZodIssueCode.invalid_return_type) {
return [
issue.message,
...issue.returnTypeError.issues.map(
(issue2) => getMessageFromZodIssue({
issue: issue2,
issueSeparator,
unionSeparator,
includePath
})
)
].join(issueSeparator);
}
if (includePath && isNonEmptyArray(issue.path)) {
if (issue.path.length === 1) {
const identifier = issue.path[0];
if (typeof identifier === "number") {
return `${issue.message} at index ${identifier}`;
}
}
return `${issue.message} at "${joinPath(issue.path)}"`;
}
return issue.message;
}
function prefixMessage(message, prefix, prefixSeparator) {
if (prefix !== null) {
if (message.length > 0) {
return [prefix, message].join(prefixSeparator);
}
return prefix;
}
if (message.length > 0) {
return message;
}
return PREFIX;
}
// lib/v3/fromZodIssue.ts
function fromZodIssue(issue, options = {}) {
const messageBuilder = createMessageBuilderFromOptions(options);
const message = messageBuilder([issue]);
return new ValidationError(message, { cause: new zod2.ZodError([issue]) });
}
function createMessageBuilderFromOptions(options) {
if ("messageBuilder" in options) {
return options.messageBuilder;
}
return createMessageBuilder(options);
}
// lib/v3/errorMap.ts
var errorMap = (issue, ctx) => {
const error = fromZodIssue({
...issue,
// fallback to the default error message
// when issue does not have a message
message: issue.message ?? ctx.defaultError
});
return {
message: error.message
};
};
// lib/v3/fromZodError.ts
function fromZodError(zodError, options = {}) {
if (!isZodErrorLike(zodError)) {
throw new TypeError(
`Invalid zodError param; expected instance of ZodError. Did you mean to use the "${fromError.name}" method instead?`
);
}
return fromZodErrorWithoutRuntimeCheck(zodError, options);
}
function fromZodErrorWithoutRuntimeCheck(zodError, options = {}) {
const zodIssues = zodError.errors;
let message;
if (isNonEmptyArray(zodIssues)) {
const messageBuilder = createMessageBuilderFromOptions2(options);
message = messageBuilder(zodIssues);
} else {
message = zodError.message;
}
return new ValidationError(message, { cause: zodError });
}
function createMessageBuilderFromOptions2(options) {
if ("messageBuilder" in options) {
return options.messageBuilder;
}
return createMessageBuilder(options);
}
// lib/v3/toValidationError.ts
var toValidationError = (options = {}) => (err) => {
if (isZodErrorLike(err)) {
return fromZodErrorWithoutRuntimeCheck(err, options);
}
if (err instanceof Error) {
return new ValidationError(err.message, { cause: err });
}
return new ValidationError("Unknown error");
};
// lib/v3/fromError.ts
function fromError(err, options = {}) {
return toValidationError(options)(err);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ValidationError,
createMessageBuilder,
errorMap,
fromError,
fromZodError,
fromZodIssue,
isValidationError,
isValidationErrorLike,
isZodErrorLike,
toValidationError
});
//# sourceMappingURL=index.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,263 @@
// lib/v3/isZodErrorLike.ts
function isZodErrorLike(err) {
return err instanceof Error && err.name === "ZodError" && "issues" in err && Array.isArray(err.issues);
}
// lib/v3/ValidationError.ts
var ValidationError = class extends Error {
name;
details;
constructor(message, options) {
super(message, options);
this.name = "ZodValidationError";
this.details = getIssuesFromErrorOptions(options);
}
toString() {
return this.message;
}
};
function getIssuesFromErrorOptions(options) {
if (options) {
const cause = options.cause;
if (isZodErrorLike(cause)) {
return cause.issues;
}
}
return [];
}
// lib/v3/isValidationError.ts
function isValidationError(err) {
return err instanceof ValidationError;
}
// lib/v3/isValidationErrorLike.ts
function isValidationErrorLike(err) {
return err instanceof Error && err.name === "ZodValidationError";
}
// lib/v3/fromZodIssue.ts
import * as zod2 from "zod/v3";
// lib/v3/MessageBuilder.ts
import * as zod from "zod/v3";
// lib/utils/NonEmptyArray.ts
function isNonEmptyArray(value) {
return value.length !== 0;
}
// lib/utils/stringify.ts
function stringifySymbol(symbol) {
return symbol.description ?? "";
}
// 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/v3/config.ts
var ISSUE_SEPARATOR = "; ";
var MAX_ISSUES_IN_MESSAGE = 99;
var PREFIX = "Validation error";
var PREFIX_SEPARATOR = ": ";
var UNION_SEPARATOR = ", or ";
// lib/v3/MessageBuilder.ts
function createMessageBuilder(props = {}) {
const {
issueSeparator = ISSUE_SEPARATOR,
unionSeparator = UNION_SEPARATOR,
prefixSeparator = PREFIX_SEPARATOR,
prefix = PREFIX,
includePath = true,
maxIssuesInMessage = MAX_ISSUES_IN_MESSAGE
} = props;
return (issues) => {
const message = issues.slice(0, maxIssuesInMessage).map(
(issue) => getMessageFromZodIssue({
issue,
issueSeparator,
unionSeparator,
includePath
})
).join(issueSeparator);
return prefixMessage(message, prefix, prefixSeparator);
};
}
function getMessageFromZodIssue(props) {
const { issue, issueSeparator, unionSeparator, includePath } = props;
if (issue.code === zod.ZodIssueCode.invalid_union) {
return issue.unionErrors.reduce((acc, zodError) => {
const newIssues = zodError.issues.map(
(issue2) => getMessageFromZodIssue({
issue: issue2,
issueSeparator,
unionSeparator,
includePath
})
).join(issueSeparator);
if (!acc.includes(newIssues)) {
acc.push(newIssues);
}
return acc;
}, []).join(unionSeparator);
}
if (issue.code === zod.ZodIssueCode.invalid_arguments) {
return [
issue.message,
...issue.argumentsError.issues.map(
(issue2) => getMessageFromZodIssue({
issue: issue2,
issueSeparator,
unionSeparator,
includePath
})
)
].join(issueSeparator);
}
if (issue.code === zod.ZodIssueCode.invalid_return_type) {
return [
issue.message,
...issue.returnTypeError.issues.map(
(issue2) => getMessageFromZodIssue({
issue: issue2,
issueSeparator,
unionSeparator,
includePath
})
)
].join(issueSeparator);
}
if (includePath && isNonEmptyArray(issue.path)) {
if (issue.path.length === 1) {
const identifier = issue.path[0];
if (typeof identifier === "number") {
return `${issue.message} at index ${identifier}`;
}
}
return `${issue.message} at "${joinPath(issue.path)}"`;
}
return issue.message;
}
function prefixMessage(message, prefix, prefixSeparator) {
if (prefix !== null) {
if (message.length > 0) {
return [prefix, message].join(prefixSeparator);
}
return prefix;
}
if (message.length > 0) {
return message;
}
return PREFIX;
}
// lib/v3/fromZodIssue.ts
function fromZodIssue(issue, options = {}) {
const messageBuilder = createMessageBuilderFromOptions(options);
const message = messageBuilder([issue]);
return new ValidationError(message, { cause: new zod2.ZodError([issue]) });
}
function createMessageBuilderFromOptions(options) {
if ("messageBuilder" in options) {
return options.messageBuilder;
}
return createMessageBuilder(options);
}
// lib/v3/errorMap.ts
var errorMap = (issue, ctx) => {
const error = fromZodIssue({
...issue,
// fallback to the default error message
// when issue does not have a message
message: issue.message ?? ctx.defaultError
});
return {
message: error.message
};
};
// lib/v3/fromZodError.ts
function fromZodError(zodError, options = {}) {
if (!isZodErrorLike(zodError)) {
throw new TypeError(
`Invalid zodError param; expected instance of ZodError. Did you mean to use the "${fromError.name}" method instead?`
);
}
return fromZodErrorWithoutRuntimeCheck(zodError, options);
}
function fromZodErrorWithoutRuntimeCheck(zodError, options = {}) {
const zodIssues = zodError.errors;
let message;
if (isNonEmptyArray(zodIssues)) {
const messageBuilder = createMessageBuilderFromOptions2(options);
message = messageBuilder(zodIssues);
} else {
message = zodError.message;
}
return new ValidationError(message, { cause: zodError });
}
function createMessageBuilderFromOptions2(options) {
if ("messageBuilder" in options) {
return options.messageBuilder;
}
return createMessageBuilder(options);
}
// lib/v3/toValidationError.ts
var toValidationError = (options = {}) => (err) => {
if (isZodErrorLike(err)) {
return fromZodErrorWithoutRuntimeCheck(err, options);
}
if (err instanceof Error) {
return new ValidationError(err.message, { cause: err });
}
return new ValidationError("Unknown error");
};
// lib/v3/fromError.ts
function fromError(err, options = {}) {
return toValidationError(options)(err);
}
export {
ValidationError,
createMessageBuilder,
errorMap,
fromError,
fromZodError,
fromZodIssue,
isValidationError,
isValidationErrorLike,
isZodErrorLike,
toValidationError
};
//# sourceMappingURL=index.mjs.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,66 @@
import * as zod from 'zod/v4/core';
declare const ZOD_VALIDATION_ERROR_NAME = "ZodValidationError";
interface ErrorOptions {
cause?: unknown;
}
declare class ValidationError extends Error {
name: typeof ZOD_VALIDATION_ERROR_NAME;
details: Array<zod.$ZodIssue>;
constructor(message?: string, options?: ErrorOptions);
toString(): string;
}
declare function isValidationError(err: unknown): err is ValidationError;
declare function isValidationErrorLike(err: unknown): err is ValidationError;
declare function isZodErrorLike(err: unknown): err is zod.$ZodError;
type ErrorMapOptions = {
dateLocalization: boolean | Intl.LocalesArgument;
numberLocalization: boolean | Intl.LocalesArgument;
displayInvalidFormatDetails: boolean;
allowedValuesSeparator: string;
allowedValuesLastSeparator: string | undefined;
wrapAllowedValuesInQuote: boolean;
maxAllowedValuesToDisplay: number;
unrecognizedKeysSeparator: string;
unrecognizedKeysLastSeparator: string | undefined;
wrapUnrecognizedKeysInQuote: boolean;
maxUnrecognizedKeysToDisplay: number;
};
declare function createErrorMap(partialOptions?: Partial<ErrorMapOptions>): zod.$ZodErrorMap<zod.$ZodIssue>;
type NonEmptyArray<T> = [T, ...T[]];
type ZodIssue = zod.$ZodIssue;
type MessageBuilder = (issues: NonEmptyArray<ZodIssue>) => string;
type MessageBuilderOptions = {
prefix: string | null | undefined;
prefixSeparator: string;
maxIssuesInMessage: number;
issueSeparator: string;
unionSeparator: string;
includePath: boolean;
forceTitleCase: boolean;
};
declare function createMessageBuilder(partialOptions?: Partial<MessageBuilderOptions>): MessageBuilder;
type ZodError = zod.$ZodError;
type FromZodErrorOptions = {
messageBuilder: MessageBuilder;
} | Partial<MessageBuilderOptions>;
declare function fromZodError(zodError: ZodError, options?: FromZodErrorOptions): ValidationError;
declare function fromError(err: unknown, options?: FromZodErrorOptions): ValidationError;
type FromZodIssueOptions = {
messageBuilder: MessageBuilder;
} | Partial<Omit<MessageBuilderOptions, 'maxIssuesInMessage'>>;
declare function fromZodIssue(issue: ZodIssue, options?: FromZodIssueOptions): ValidationError;
declare const toValidationError: (options?: FromZodErrorOptions) => (err: unknown) => ValidationError;
export { type ErrorMapOptions, type ErrorOptions, type FromZodErrorOptions, type FromZodIssueOptions, type MessageBuilder, type MessageBuilderOptions, type NonEmptyArray, ValidationError, type ZodError, type ZodIssue, createErrorMap, createMessageBuilder, fromError, fromZodError, fromZodIssue, isValidationError, isValidationErrorLike, isZodErrorLike, toValidationError };
@@ -0,0 +1,66 @@
import * as zod from 'zod/v4/core';
declare const ZOD_VALIDATION_ERROR_NAME = "ZodValidationError";
interface ErrorOptions {
cause?: unknown;
}
declare class ValidationError extends Error {
name: typeof ZOD_VALIDATION_ERROR_NAME;
details: Array<zod.$ZodIssue>;
constructor(message?: string, options?: ErrorOptions);
toString(): string;
}
declare function isValidationError(err: unknown): err is ValidationError;
declare function isValidationErrorLike(err: unknown): err is ValidationError;
declare function isZodErrorLike(err: unknown): err is zod.$ZodError;
type ErrorMapOptions = {
dateLocalization: boolean | Intl.LocalesArgument;
numberLocalization: boolean | Intl.LocalesArgument;
displayInvalidFormatDetails: boolean;
allowedValuesSeparator: string;
allowedValuesLastSeparator: string | undefined;
wrapAllowedValuesInQuote: boolean;
maxAllowedValuesToDisplay: number;
unrecognizedKeysSeparator: string;
unrecognizedKeysLastSeparator: string | undefined;
wrapUnrecognizedKeysInQuote: boolean;
maxUnrecognizedKeysToDisplay: number;
};
declare function createErrorMap(partialOptions?: Partial<ErrorMapOptions>): zod.$ZodErrorMap<zod.$ZodIssue>;
type NonEmptyArray<T> = [T, ...T[]];
type ZodIssue = zod.$ZodIssue;
type MessageBuilder = (issues: NonEmptyArray<ZodIssue>) => string;
type MessageBuilderOptions = {
prefix: string | null | undefined;
prefixSeparator: string;
maxIssuesInMessage: number;
issueSeparator: string;
unionSeparator: string;
includePath: boolean;
forceTitleCase: boolean;
};
declare function createMessageBuilder(partialOptions?: Partial<MessageBuilderOptions>): MessageBuilder;
type ZodError = zod.$ZodError;
type FromZodErrorOptions = {
messageBuilder: MessageBuilder;
} | Partial<MessageBuilderOptions>;
declare function fromZodError(zodError: ZodError, options?: FromZodErrorOptions): ValidationError;
declare function fromError(err: unknown, options?: FromZodErrorOptions): ValidationError;
type FromZodIssueOptions = {
messageBuilder: MessageBuilder;
} | Partial<Omit<MessageBuilderOptions, 'maxIssuesInMessage'>>;
declare function fromZodIssue(issue: ZodIssue, options?: FromZodIssueOptions): ValidationError;
declare const toValidationError: (options?: FromZodErrorOptions) => (err: unknown) => ValidationError;
export { type ErrorMapOptions, type ErrorOptions, type FromZodErrorOptions, type FromZodIssueOptions, type MessageBuilder, type MessageBuilderOptions, type NonEmptyArray, ValidationError, type ZodError, type ZodIssue, createErrorMap, createMessageBuilder, fromError, fromZodError, fromZodIssue, isValidationError, isValidationErrorLike, isZodErrorLike, toValidationError };
@@ -0,0 +1,725 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// lib/v4/index.ts
var index_exports = {};
__export(index_exports, {
ValidationError: () => ValidationError,
createErrorMap: () => createErrorMap,
createMessageBuilder: () => createMessageBuilder,
fromError: () => fromError,
fromZodError: () => fromZodError,
fromZodIssue: () => fromZodIssue,
isValidationError: () => isValidationError,
isValidationErrorLike: () => isValidationErrorLike,
isZodErrorLike: () => isZodErrorLike,
toValidationError: () => toValidationError
});
module.exports = __toCommonJS(index_exports);
// lib/v4/isZodErrorLike.ts
function isZodErrorLike(err) {
return err instanceof Object && "name" in err && (err.name === "ZodError" || err.name === "$ZodError") && "issues" in err && Array.isArray(err.issues);
}
// lib/v4/ValidationError.ts
var ZOD_VALIDATION_ERROR_NAME = "ZodValidationError";
var ValidationError = class extends Error {
name;
details;
constructor(message, options) {
super(message, options);
this.name = ZOD_VALIDATION_ERROR_NAME;
this.details = getIssuesFromErrorOptions(options);
}
toString() {
return this.message;
}
};
function getIssuesFromErrorOptions(options) {
if (options) {
const cause = options.cause;
if (isZodErrorLike(cause)) {
return cause.issues;
}
}
return [];
}
// lib/v4/isValidationError.ts
function isValidationError(err) {
return err instanceof ValidationError;
}
// lib/v4/isValidationErrorLike.ts
function isValidationErrorLike(err) {
return err instanceof Error && err.name === ZOD_VALIDATION_ERROR_NAME;
}
// lib/v4/errorMap/custom.ts
function parseCustomIssue(issue) {
return {
type: issue.code,
path: issue.path,
message: issue.message ?? "Invalid input"
};
}
// lib/v4/errorMap/invalidElement.ts
function parseInvalidElementIssue(issue) {
return {
type: issue.code,
path: issue.path,
message: `unexpected element in ${issue.origin}`
};
}
// 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
function parseInvalidStringFormatIssue(issue, options = {
displayInvalidFormatDetails: false
}) {
switch (issue.format) {
case "lowercase":
case "uppercase":
return {
type: issue.code,
path: issue.path,
message: `value must be in ${issue.format} format`
};
default: {
if (isZodIssueStringStartsWith(issue)) {
return parseStringStartsWith(issue);
}
if (isZodIssueStringEndsWith(issue)) {
return parseStringEndsWith(issue);
}
if (isZodIssueStringIncludes(issue)) {
return parseStringIncludes(issue);
}
if (isZodIssueStringInvalidRegex(issue)) {
return parseStringInvalidRegex(issue, options);
}
if (isZodIssueStringInvalidJWT(issue)) {
return parseStringInvalidJWT(issue, options);
}
return {
type: issue.code,
path: issue.path,
message: `invalid ${issue.format}`
};
}
}
}
function isZodIssueStringStartsWith(issue) {
return issue.format === "starts_with";
}
function parseStringStartsWith(issue) {
return {
type: issue.code,
path: issue.path,
message: `value must start with "${issue.prefix}"`
};
}
function isZodIssueStringEndsWith(issue) {
return issue.format === "ends_with";
}
function parseStringEndsWith(issue) {
return {
type: issue.code,
path: issue.path,
message: `value must end with "${issue.suffix}"`
};
}
function isZodIssueStringIncludes(issue) {
return issue.format === "includes";
}
function parseStringIncludes(issue) {
return {
type: issue.code,
path: issue.path,
message: `value must include "${issue.includes}"`
};
}
function isZodIssueStringInvalidRegex(issue) {
return issue.format === "regex";
}
function parseStringInvalidRegex(issue, options = {
displayInvalidFormatDetails: false
}) {
let message = "value must match pattern";
if (options.displayInvalidFormatDetails) {
message += ` "${issue.pattern}"`;
}
return {
type: issue.code,
path: issue.path,
message
};
}
function isZodIssueStringInvalidJWT(issue) {
return issue.format === "jwt";
}
function parseStringInvalidJWT(issue, options = {
displayInvalidFormatDetails: false
}) {
return {
type: issue.code,
path: issue.path,
message: options.displayInvalidFormatDetails && issue.algorithm ? `invalid jwt/${issue.algorithm}` : `invalid jwt`
};
}
// lib/v4/errorMap/invalidType.ts
function parseInvalidTypeIssue(issue) {
let message = `expected ${issue.expected}`;
if ("input" in issue) {
message += `, received ${getTypeName(issue.input)}`;
}
return {
type: issue.code,
path: issue.path,
message
};
}
function getTypeName(value) {
if (typeof value === "object") {
if (value === null) {
return "null";
}
if (value === void 0) {
return "undefined";
}
if (Array.isArray(value)) {
return "array";
}
if (value instanceof Date) {
return "date";
}
if (value instanceof RegExp) {
return "regexp";
}
if (value instanceof Map) {
return "map";
}
if (value instanceof Set) {
return "set";
}
if (value instanceof Error) {
return "error";
}
if (value instanceof Function) {
return "function";
}
return "object";
}
return typeof value;
}
// lib/v4/errorMap/invalidUnion.ts
function parseInvalidUnionIssue(issue) {
return {
type: issue.code,
path: issue.path,
message: issue.message ?? "Invalid input"
};
}
// 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
function joinValues(values, options) {
const valuesToDisplay = (options.maxValuesToDisplay ? values.slice(0, options.maxValuesToDisplay) : values).map((value) => {
return stringify(value, {
wrapStringValueInQuote: options.wrapStringValuesInQuote
});
});
if (valuesToDisplay.length < values.length) {
valuesToDisplay.push(
`${values.length - valuesToDisplay.length} more value(s)`
);
}
return valuesToDisplay.reduce((acc, value, index) => {
if (index > 0) {
if (index === valuesToDisplay.length - 1 && options.lastSeparator) {
acc += options.lastSeparator;
} else {
acc += options.separator;
}
}
acc += value;
return acc;
}, "");
}
// lib/v4/errorMap/invalidValue.ts
function parseInvalidValueIssue(issue, options) {
let message;
if (issue.values.length === 0) {
message = "invalid value";
} else if (issue.values.length === 1) {
const valueStr = stringify(issue.values[0], {
wrapStringValueInQuote: true
});
message = `expected value to be ${valueStr}`;
} else {
const valuesStr = joinValues(issue.values, {
separator: options.allowedValuesSeparator,
lastSeparator: options.allowedValuesLastSeparator,
wrapStringValuesInQuote: options.wrapAllowedValuesInQuote,
maxValuesToDisplay: options.maxAllowedValuesToDisplay
});
message = `expected value to be one of ${valuesStr}`;
}
return {
type: issue.code,
path: issue.path,
message
};
}
// lib/v4/errorMap/notMultipleOf.ts
function parseNotMultipleOfIssue(issue) {
return {
type: issue.code,
path: issue.path,
message: `expected multiple of ${issue.divisor}`
};
}
// lib/v4/errorMap/tooBig.ts
function parseTooBigIssue(issue, options) {
const maxValueStr = issue.origin === "date" ? stringify(new Date(issue.maximum), {
localization: options.dateLocalization
}) : stringify(issue.maximum, {
localization: options.numberLocalization
});
switch (issue.origin) {
case "number":
case "int":
case "bigint": {
return {
type: issue.code,
path: issue.path,
message: `number must be less than${issue.inclusive ? " or equal to" : ""} ${maxValueStr}`
};
}
case "string": {
return {
type: issue.code,
path: issue.path,
message: `string must contain at most ${maxValueStr} character(s)`
};
}
case "date": {
return {
type: issue.code,
path: issue.path,
message: `date must be ${issue.inclusive ? "prior or equal to" : "prior to"} "${maxValueStr}"`
};
}
case "array": {
return {
type: issue.code,
path: issue.path,
message: `array must contain at most ${maxValueStr} item(s)`
};
}
case "set": {
return {
type: issue.code,
path: issue.path,
message: `set must contain at most ${maxValueStr} item(s)`
};
}
case "file": {
return {
type: issue.code,
path: issue.path,
message: `file must not exceed ${maxValueStr} byte(s) in size`
};
}
default:
return {
type: issue.code,
path: issue.path,
message: `value must be less than${issue.inclusive ? " or equal to" : ""} ${maxValueStr}`
};
}
}
// lib/v4/errorMap/tooSmall.ts
function parseTooSmallIssue(issue, options) {
const minValueStr = issue.origin === "date" ? stringify(new Date(issue.minimum), {
localization: options.dateLocalization
}) : stringify(issue.minimum, {
localization: options.numberLocalization
});
switch (issue.origin) {
case "number":
case "int":
case "bigint": {
return {
type: issue.code,
path: issue.path,
message: `number must be greater than${issue.inclusive ? " or equal to" : ""} ${minValueStr}`
};
}
case "date": {
return {
type: issue.code,
path: issue.path,
message: `date must be ${issue.inclusive ? "later or equal to" : "later to"} "${minValueStr}"`
};
}
case "string": {
return {
type: issue.code,
path: issue.path,
message: `string must contain at least ${minValueStr} character(s)`
};
}
case "array": {
return {
type: issue.code,
path: issue.path,
message: `array must contain at least ${minValueStr} item(s)`
};
}
case "set": {
return {
type: issue.code,
path: issue.path,
message: `set must contain at least ${minValueStr} item(s)`
};
}
case "file": {
return {
type: issue.code,
path: issue.path,
message: `file must be at least ${minValueStr} byte(s) in size`
};
}
default:
return {
type: issue.code,
path: issue.path,
message: `value must be greater than${issue.inclusive ? " or equal to" : ""} ${minValueStr}`
};
}
}
// lib/v4/errorMap/unrecognizedKeys.ts
function parseUnrecognizedKeysIssue(issue, options) {
const keysStr = joinValues(issue.keys, {
separator: options.unrecognizedKeysSeparator,
lastSeparator: options.unrecognizedKeysLastSeparator,
wrapStringValuesInQuote: options.wrapUnrecognizedKeysInQuote,
maxValuesToDisplay: options.maxUnrecognizedKeysToDisplay
});
return {
type: issue.code,
path: issue.path,
message: `unrecognized key(s) ${keysStr} in object`
};
}
// lib/v4/errorMap/errorMap.ts
var issueParsers = {
invalid_type: parseInvalidTypeIssue,
too_big: parseTooBigIssue,
too_small: parseTooSmallIssue,
invalid_format: parseInvalidStringFormatIssue,
invalid_value: parseInvalidValueIssue,
invalid_element: parseInvalidElementIssue,
not_multiple_of: parseNotMultipleOfIssue,
unrecognized_keys: parseUnrecognizedKeysIssue,
invalid_key: parseInvalidKeyIssue,
custom: parseCustomIssue,
invalid_union: parseInvalidUnionIssue
};
var defaultErrorMapOptions = {
displayInvalidFormatDetails: false,
allowedValuesSeparator: ", ",
allowedValuesLastSeparator: " or ",
wrapAllowedValuesInQuote: true,
maxAllowedValuesToDisplay: 10,
unrecognizedKeysSeparator: ", ",
unrecognizedKeysLastSeparator: " and ",
wrapUnrecognizedKeysInQuote: true,
maxUnrecognizedKeysToDisplay: 5,
dateLocalization: true,
numberLocalization: true
};
function createErrorMap(partialOptions = {}) {
const options = {
...defaultErrorMapOptions,
...partialOptions
};
const errorMap = (issue) => {
if (issue.code === void 0) {
return "Not supported issue type";
}
const parseFunc = issueParsers[issue.code];
const ast = parseFunc(issue, options);
return ast.message;
};
return errorMap;
}
// lib/utils/NonEmptyArray.ts
function isNonEmptyArray(value) {
return value.length !== 0;
}
// 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/titleCase.ts
function titleCase(value) {
if (value.length === 0) {
return value;
}
return value.charAt(0).toUpperCase() + value.slice(1);
}
// lib/v4/MessageBuilder.ts
var defaultMessageBuilderOptions = {
prefix: "Validation error",
prefixSeparator: ": ",
maxIssuesInMessage: 99,
// I've got 99 problems but the b$tch ain't one
unionSeparator: " or ",
issueSeparator: "; ",
includePath: true,
forceTitleCase: true
};
function createMessageBuilder(partialOptions = {}) {
const options = {
...defaultMessageBuilderOptions,
...partialOptions
};
return function messageBuilder(issues) {
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" && isNonEmptyArray(issue.errors)) {
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) {
if (options.prefix != null) {
if (message.length > 0) {
return [options.prefix, message].join(options.prefixSeparator);
}
return options.prefix;
}
if (message.length > 0) {
return message;
}
return defaultMessageBuilderOptions.prefix;
}
// lib/v4/fromZodError.ts
function fromZodError(zodError, options = {}) {
if (!isZodErrorLike(zodError)) {
throw new TypeError(
`Invalid zodError param; expected instance of ZodError. Did you mean to use the "${fromError.name}" method instead?`
);
}
return fromZodErrorWithoutRuntimeCheck(zodError, options);
}
function fromZodErrorWithoutRuntimeCheck(zodError, options = {}) {
const zodIssues = zodError.issues;
let message;
if (isNonEmptyArray(zodIssues)) {
const messageBuilder = createMessageBuilderFromOptions(options);
message = messageBuilder(zodIssues);
} else {
message = zodError.message;
}
return new ValidationError(message, { cause: zodError });
}
function createMessageBuilderFromOptions(options) {
if ("messageBuilder" in options) {
return options.messageBuilder;
}
return createMessageBuilder(options);
}
// lib/v4/toValidationError.ts
var toValidationError = (options = {}) => (err) => {
if (isZodErrorLike(err)) {
return fromZodErrorWithoutRuntimeCheck(err, options);
}
if (err instanceof Error) {
return new ValidationError(err.message, { cause: err });
}
return new ValidationError("Unknown error");
};
// lib/v4/fromError.ts
function fromError(err, options = {}) {
return toValidationError(options)(err);
}
// lib/v4/fromZodIssue.ts
var zod = __toESM(require("zod/v4/core"));
function fromZodIssue(issue, options = {}) {
const messageBuilder = createMessageBuilderFromOptions2(options);
const message = messageBuilder([issue]);
return new ValidationError(message, {
cause: new zod.$ZodRealError([issue])
});
}
function createMessageBuilderFromOptions2(options) {
if ("messageBuilder" in options) {
return options.messageBuilder;
}
return createMessageBuilder(options);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ValidationError,
createErrorMap,
createMessageBuilder,
fromError,
fromZodError,
fromZodIssue,
isValidationError,
isValidationErrorLike,
isZodErrorLike,
toValidationError
});
//# sourceMappingURL=index.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,679 @@
// lib/v4/isZodErrorLike.ts
function isZodErrorLike(err) {
return err instanceof Object && "name" in err && (err.name === "ZodError" || err.name === "$ZodError") && "issues" in err && Array.isArray(err.issues);
}
// lib/v4/ValidationError.ts
var ZOD_VALIDATION_ERROR_NAME = "ZodValidationError";
var ValidationError = class extends Error {
name;
details;
constructor(message, options) {
super(message, options);
this.name = ZOD_VALIDATION_ERROR_NAME;
this.details = getIssuesFromErrorOptions(options);
}
toString() {
return this.message;
}
};
function getIssuesFromErrorOptions(options) {
if (options) {
const cause = options.cause;
if (isZodErrorLike(cause)) {
return cause.issues;
}
}
return [];
}
// lib/v4/isValidationError.ts
function isValidationError(err) {
return err instanceof ValidationError;
}
// lib/v4/isValidationErrorLike.ts
function isValidationErrorLike(err) {
return err instanceof Error && err.name === ZOD_VALIDATION_ERROR_NAME;
}
// lib/v4/errorMap/custom.ts
function parseCustomIssue(issue) {
return {
type: issue.code,
path: issue.path,
message: issue.message ?? "Invalid input"
};
}
// lib/v4/errorMap/invalidElement.ts
function parseInvalidElementIssue(issue) {
return {
type: issue.code,
path: issue.path,
message: `unexpected element in ${issue.origin}`
};
}
// 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
function parseInvalidStringFormatIssue(issue, options = {
displayInvalidFormatDetails: false
}) {
switch (issue.format) {
case "lowercase":
case "uppercase":
return {
type: issue.code,
path: issue.path,
message: `value must be in ${issue.format} format`
};
default: {
if (isZodIssueStringStartsWith(issue)) {
return parseStringStartsWith(issue);
}
if (isZodIssueStringEndsWith(issue)) {
return parseStringEndsWith(issue);
}
if (isZodIssueStringIncludes(issue)) {
return parseStringIncludes(issue);
}
if (isZodIssueStringInvalidRegex(issue)) {
return parseStringInvalidRegex(issue, options);
}
if (isZodIssueStringInvalidJWT(issue)) {
return parseStringInvalidJWT(issue, options);
}
return {
type: issue.code,
path: issue.path,
message: `invalid ${issue.format}`
};
}
}
}
function isZodIssueStringStartsWith(issue) {
return issue.format === "starts_with";
}
function parseStringStartsWith(issue) {
return {
type: issue.code,
path: issue.path,
message: `value must start with "${issue.prefix}"`
};
}
function isZodIssueStringEndsWith(issue) {
return issue.format === "ends_with";
}
function parseStringEndsWith(issue) {
return {
type: issue.code,
path: issue.path,
message: `value must end with "${issue.suffix}"`
};
}
function isZodIssueStringIncludes(issue) {
return issue.format === "includes";
}
function parseStringIncludes(issue) {
return {
type: issue.code,
path: issue.path,
message: `value must include "${issue.includes}"`
};
}
function isZodIssueStringInvalidRegex(issue) {
return issue.format === "regex";
}
function parseStringInvalidRegex(issue, options = {
displayInvalidFormatDetails: false
}) {
let message = "value must match pattern";
if (options.displayInvalidFormatDetails) {
message += ` "${issue.pattern}"`;
}
return {
type: issue.code,
path: issue.path,
message
};
}
function isZodIssueStringInvalidJWT(issue) {
return issue.format === "jwt";
}
function parseStringInvalidJWT(issue, options = {
displayInvalidFormatDetails: false
}) {
return {
type: issue.code,
path: issue.path,
message: options.displayInvalidFormatDetails && issue.algorithm ? `invalid jwt/${issue.algorithm}` : `invalid jwt`
};
}
// lib/v4/errorMap/invalidType.ts
function parseInvalidTypeIssue(issue) {
let message = `expected ${issue.expected}`;
if ("input" in issue) {
message += `, received ${getTypeName(issue.input)}`;
}
return {
type: issue.code,
path: issue.path,
message
};
}
function getTypeName(value) {
if (typeof value === "object") {
if (value === null) {
return "null";
}
if (value === void 0) {
return "undefined";
}
if (Array.isArray(value)) {
return "array";
}
if (value instanceof Date) {
return "date";
}
if (value instanceof RegExp) {
return "regexp";
}
if (value instanceof Map) {
return "map";
}
if (value instanceof Set) {
return "set";
}
if (value instanceof Error) {
return "error";
}
if (value instanceof Function) {
return "function";
}
return "object";
}
return typeof value;
}
// lib/v4/errorMap/invalidUnion.ts
function parseInvalidUnionIssue(issue) {
return {
type: issue.code,
path: issue.path,
message: issue.message ?? "Invalid input"
};
}
// 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
function joinValues(values, options) {
const valuesToDisplay = (options.maxValuesToDisplay ? values.slice(0, options.maxValuesToDisplay) : values).map((value) => {
return stringify(value, {
wrapStringValueInQuote: options.wrapStringValuesInQuote
});
});
if (valuesToDisplay.length < values.length) {
valuesToDisplay.push(
`${values.length - valuesToDisplay.length} more value(s)`
);
}
return valuesToDisplay.reduce((acc, value, index) => {
if (index > 0) {
if (index === valuesToDisplay.length - 1 && options.lastSeparator) {
acc += options.lastSeparator;
} else {
acc += options.separator;
}
}
acc += value;
return acc;
}, "");
}
// lib/v4/errorMap/invalidValue.ts
function parseInvalidValueIssue(issue, options) {
let message;
if (issue.values.length === 0) {
message = "invalid value";
} else if (issue.values.length === 1) {
const valueStr = stringify(issue.values[0], {
wrapStringValueInQuote: true
});
message = `expected value to be ${valueStr}`;
} else {
const valuesStr = joinValues(issue.values, {
separator: options.allowedValuesSeparator,
lastSeparator: options.allowedValuesLastSeparator,
wrapStringValuesInQuote: options.wrapAllowedValuesInQuote,
maxValuesToDisplay: options.maxAllowedValuesToDisplay
});
message = `expected value to be one of ${valuesStr}`;
}
return {
type: issue.code,
path: issue.path,
message
};
}
// lib/v4/errorMap/notMultipleOf.ts
function parseNotMultipleOfIssue(issue) {
return {
type: issue.code,
path: issue.path,
message: `expected multiple of ${issue.divisor}`
};
}
// lib/v4/errorMap/tooBig.ts
function parseTooBigIssue(issue, options) {
const maxValueStr = issue.origin === "date" ? stringify(new Date(issue.maximum), {
localization: options.dateLocalization
}) : stringify(issue.maximum, {
localization: options.numberLocalization
});
switch (issue.origin) {
case "number":
case "int":
case "bigint": {
return {
type: issue.code,
path: issue.path,
message: `number must be less than${issue.inclusive ? " or equal to" : ""} ${maxValueStr}`
};
}
case "string": {
return {
type: issue.code,
path: issue.path,
message: `string must contain at most ${maxValueStr} character(s)`
};
}
case "date": {
return {
type: issue.code,
path: issue.path,
message: `date must be ${issue.inclusive ? "prior or equal to" : "prior to"} "${maxValueStr}"`
};
}
case "array": {
return {
type: issue.code,
path: issue.path,
message: `array must contain at most ${maxValueStr} item(s)`
};
}
case "set": {
return {
type: issue.code,
path: issue.path,
message: `set must contain at most ${maxValueStr} item(s)`
};
}
case "file": {
return {
type: issue.code,
path: issue.path,
message: `file must not exceed ${maxValueStr} byte(s) in size`
};
}
default:
return {
type: issue.code,
path: issue.path,
message: `value must be less than${issue.inclusive ? " or equal to" : ""} ${maxValueStr}`
};
}
}
// lib/v4/errorMap/tooSmall.ts
function parseTooSmallIssue(issue, options) {
const minValueStr = issue.origin === "date" ? stringify(new Date(issue.minimum), {
localization: options.dateLocalization
}) : stringify(issue.minimum, {
localization: options.numberLocalization
});
switch (issue.origin) {
case "number":
case "int":
case "bigint": {
return {
type: issue.code,
path: issue.path,
message: `number must be greater than${issue.inclusive ? " or equal to" : ""} ${minValueStr}`
};
}
case "date": {
return {
type: issue.code,
path: issue.path,
message: `date must be ${issue.inclusive ? "later or equal to" : "later to"} "${minValueStr}"`
};
}
case "string": {
return {
type: issue.code,
path: issue.path,
message: `string must contain at least ${minValueStr} character(s)`
};
}
case "array": {
return {
type: issue.code,
path: issue.path,
message: `array must contain at least ${minValueStr} item(s)`
};
}
case "set": {
return {
type: issue.code,
path: issue.path,
message: `set must contain at least ${minValueStr} item(s)`
};
}
case "file": {
return {
type: issue.code,
path: issue.path,
message: `file must be at least ${minValueStr} byte(s) in size`
};
}
default:
return {
type: issue.code,
path: issue.path,
message: `value must be greater than${issue.inclusive ? " or equal to" : ""} ${minValueStr}`
};
}
}
// lib/v4/errorMap/unrecognizedKeys.ts
function parseUnrecognizedKeysIssue(issue, options) {
const keysStr = joinValues(issue.keys, {
separator: options.unrecognizedKeysSeparator,
lastSeparator: options.unrecognizedKeysLastSeparator,
wrapStringValuesInQuote: options.wrapUnrecognizedKeysInQuote,
maxValuesToDisplay: options.maxUnrecognizedKeysToDisplay
});
return {
type: issue.code,
path: issue.path,
message: `unrecognized key(s) ${keysStr} in object`
};
}
// lib/v4/errorMap/errorMap.ts
var issueParsers = {
invalid_type: parseInvalidTypeIssue,
too_big: parseTooBigIssue,
too_small: parseTooSmallIssue,
invalid_format: parseInvalidStringFormatIssue,
invalid_value: parseInvalidValueIssue,
invalid_element: parseInvalidElementIssue,
not_multiple_of: parseNotMultipleOfIssue,
unrecognized_keys: parseUnrecognizedKeysIssue,
invalid_key: parseInvalidKeyIssue,
custom: parseCustomIssue,
invalid_union: parseInvalidUnionIssue
};
var defaultErrorMapOptions = {
displayInvalidFormatDetails: false,
allowedValuesSeparator: ", ",
allowedValuesLastSeparator: " or ",
wrapAllowedValuesInQuote: true,
maxAllowedValuesToDisplay: 10,
unrecognizedKeysSeparator: ", ",
unrecognizedKeysLastSeparator: " and ",
wrapUnrecognizedKeysInQuote: true,
maxUnrecognizedKeysToDisplay: 5,
dateLocalization: true,
numberLocalization: true
};
function createErrorMap(partialOptions = {}) {
const options = {
...defaultErrorMapOptions,
...partialOptions
};
const errorMap = (issue) => {
if (issue.code === void 0) {
return "Not supported issue type";
}
const parseFunc = issueParsers[issue.code];
const ast = parseFunc(issue, options);
return ast.message;
};
return errorMap;
}
// lib/utils/NonEmptyArray.ts
function isNonEmptyArray(value) {
return value.length !== 0;
}
// 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/titleCase.ts
function titleCase(value) {
if (value.length === 0) {
return value;
}
return value.charAt(0).toUpperCase() + value.slice(1);
}
// lib/v4/MessageBuilder.ts
var defaultMessageBuilderOptions = {
prefix: "Validation error",
prefixSeparator: ": ",
maxIssuesInMessage: 99,
// I've got 99 problems but the b$tch ain't one
unionSeparator: " or ",
issueSeparator: "; ",
includePath: true,
forceTitleCase: true
};
function createMessageBuilder(partialOptions = {}) {
const options = {
...defaultMessageBuilderOptions,
...partialOptions
};
return function messageBuilder(issues) {
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" && isNonEmptyArray(issue.errors)) {
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) {
if (options.prefix != null) {
if (message.length > 0) {
return [options.prefix, message].join(options.prefixSeparator);
}
return options.prefix;
}
if (message.length > 0) {
return message;
}
return defaultMessageBuilderOptions.prefix;
}
// lib/v4/fromZodError.ts
function fromZodError(zodError, options = {}) {
if (!isZodErrorLike(zodError)) {
throw new TypeError(
`Invalid zodError param; expected instance of ZodError. Did you mean to use the "${fromError.name}" method instead?`
);
}
return fromZodErrorWithoutRuntimeCheck(zodError, options);
}
function fromZodErrorWithoutRuntimeCheck(zodError, options = {}) {
const zodIssues = zodError.issues;
let message;
if (isNonEmptyArray(zodIssues)) {
const messageBuilder = createMessageBuilderFromOptions(options);
message = messageBuilder(zodIssues);
} else {
message = zodError.message;
}
return new ValidationError(message, { cause: zodError });
}
function createMessageBuilderFromOptions(options) {
if ("messageBuilder" in options) {
return options.messageBuilder;
}
return createMessageBuilder(options);
}
// lib/v4/toValidationError.ts
var toValidationError = (options = {}) => (err) => {
if (isZodErrorLike(err)) {
return fromZodErrorWithoutRuntimeCheck(err, options);
}
if (err instanceof Error) {
return new ValidationError(err.message, { cause: err });
}
return new ValidationError("Unknown error");
};
// lib/v4/fromError.ts
function fromError(err, options = {}) {
return toValidationError(options)(err);
}
// lib/v4/fromZodIssue.ts
import * as zod from "zod/v4/core";
function fromZodIssue(issue, options = {}) {
const messageBuilder = createMessageBuilderFromOptions2(options);
const message = messageBuilder([issue]);
return new ValidationError(message, {
cause: new zod.$ZodRealError([issue])
});
}
function createMessageBuilderFromOptions2(options) {
if ("messageBuilder" in options) {
return options.messageBuilder;
}
return createMessageBuilder(options);
}
export {
ValidationError,
createErrorMap,
createMessageBuilder,
fromError,
fromZodError,
fromZodIssue,
isValidationError,
isValidationErrorLike,
isZodErrorLike,
toValidationError
};
//# sourceMappingURL=index.mjs.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Colin McDonnell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,191 @@
<p align="center">
<img src="logo.svg" width="200px" align="center" alt="Zod logo" />
<h1 align="center">Zod</h1>
<p align="center">
TypeScript-first schema validation with static type inference
<br/>
by <a href="https://x.com/colinhacks">@colinhacks</a>
</p>
</p>
<br/>
<p align="center">
<a href="https://github.com/colinhacks/zod/actions?query=branch%3Amain"><img src="https://github.com/colinhacks/zod/actions/workflows/test.yml/badge.svg?event=push&branch=main" alt="Zod CI status" /></a>
<a href="https://opensource.org/licenses/MIT" rel="nofollow"><img src="https://img.shields.io/github/license/colinhacks/zod" alt="License"></a>
<a href="https://www.npmjs.com/package/zod" rel="nofollow"><img src="https://img.shields.io/npm/dw/zod.svg" alt="npm"></a>
<a href="https://discord.gg/KaSRdyX2vc" rel="nofollow"><img src="https://img.shields.io/discord/893487829802418277?label=Discord&logo=discord&logoColor=white" alt="discord server"></a>
<a href="https://github.com/colinhacks/zod" rel="nofollow"><img src="https://img.shields.io/github/stars/colinhacks/zod" alt="stars"></a>
</p>
<div align="center">
<a href="https://zod.dev/api">Docs</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
<a href="https://discord.gg/RcG33DQJdf">Discord</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
<a href="https://twitter.com/colinhacks">𝕏</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
<a href="https://bsky.app/profile/zod.dev">Bluesky</a>
<br />
</div>
<br/>
<br/>
### [Read the docs →](https://zod.dev/api)
<br/>
<br/>
## What is Zod?
Zod is a TypeScript-first validation library. Define a schema and parse some data with it. You'll get back a strongly typed, validated result.
```ts
import * as z from "zod";
const User = z.object({
name: z.string(),
});
// some untrusted data...
const input = {
/* stuff */
};
// the parsed result is validated and type safe!
const data = User.parse(input);
// so you can use it with confidence :)
console.log(data.name);
```
<br/>
## Features
- Zero external dependencies
- Works in Node.js and all modern browsers
- Tiny: `2kb` core bundle (gzipped)
- Immutable API: methods return a new instance
- Concise interface
- Works with TypeScript and plain JS
- Built-in JSON Schema conversion
- Extensive ecosystem
<br/>
## Installation
```sh
npm install zod
```
<br/>
## Basic usage
Before you can do anything else, you need to define a schema. For the purposes of this guide, we'll use a simple object schema.
```ts
import * as z from "zod";
const Player = z.object({
username: z.string(),
xp: z.number(),
});
```
### Parsing data
Given any Zod schema, use `.parse` to validate an input. If it's valid, Zod returns a strongly-typed _deep clone_ of the input.
```ts
Player.parse({ username: "billie", xp: 100 });
// => returns { username: "billie", xp: 100 }
```
**Note** — If your schema uses certain asynchronous APIs like `async` [refinements](https://zod.dev/api#refinements) or [transforms](https://zod.dev/api#transforms), you'll need to use the `.parseAsync()` method instead.
```ts
const schema = z.string().refine(async (val) => val.length <= 8);
await schema.parseAsync("hello");
// => "hello"
```
### Handling errors
When validation fails, the `.parse()` method will throw a `ZodError` instance with granular information about the validation issues.
```ts
try {
Player.parse({ username: 42, xp: "100" });
} catch (err) {
if (err instanceof z.ZodError) {
err.issues;
/* [
{
expected: 'string',
code: 'invalid_type',
path: [ 'username' ],
message: 'Invalid input: expected string'
},
{
expected: 'number',
code: 'invalid_type',
path: [ 'xp' ],
message: 'Invalid input: expected number'
}
] */
}
}
```
To avoid a `try/catch` block, you can use the `.safeParse()` method to get back a plain result object containing either the successfully parsed data or a `ZodError`. The result type is a [discriminated union](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions), so you can handle both cases conveniently.
```ts
const result = Player.safeParse({ username: 42, xp: "100" });
if (!result.success) {
result.error; // ZodError instance
} else {
result.data; // { username: string; xp: number }
}
```
**Note** — If your schema uses certain asynchronous APIs like `async` [refinements](https://zod.dev/api#refinements) or [transforms](https://zod.dev/api#transforms), you'll need to use the `.safeParseAsync()` method instead.
```ts
const schema = z.string().refine(async (val) => val.length <= 8);
await schema.safeParseAsync("hello");
// => { success: true; data: "hello" }
```
### Inferring types
Zod infers a static type from your schema definitions. You can extract this type with the `z.infer<>` utility and use it however you like.
```ts
const Player = z.object({
username: z.string(),
xp: z.number(),
});
// extract the inferred type
type Player = z.infer<typeof Player>;
// use it in your code
const player: Player = { username: "billie", xp: 100 };
```
In some cases, the input & output types of a schema can diverge. For instance, the `.transform()` API can convert the input from one type to another. In these cases, you can extract the input and output types independently:
```ts
const mySchema = z.string().transform((val) => val.length);
type MySchemaIn = z.input<typeof mySchema>;
// => string
type MySchemaOut = z.output<typeof mySchema>; // equivalent to z.infer<typeof mySchema>
// number
```
@@ -0,0 +1,33 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.z = void 0;
const z = __importStar(require("./v4/classic/external.cjs"));
exports.z = z;
__exportStar(require("./v4/classic/external.cjs"), exports);
exports.default = z;
@@ -0,0 +1,4 @@
import * as z from "./v4/classic/external.cjs";
export * from "./v4/classic/external.cjs";
export { z };
export default z;
@@ -0,0 +1,4 @@
import * as z from "./v4/classic/external.js";
export * from "./v4/classic/external.js";
export { z };
export default z;
@@ -0,0 +1,4 @@
import * as z from "./v4/classic/external.js";
export * from "./v4/classic/external.js";
export { z };
export default z;
@@ -0,0 +1,17 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("../v4/locales/index.cjs"), exports);
@@ -0,0 +1 @@
export * from "../v4/locales/index.cjs";
@@ -0,0 +1 @@
export * from "../v4/locales/index.js";
@@ -0,0 +1 @@
export * from "../v4/locales/index.js";
@@ -0,0 +1,7 @@
{
"type": "module",
"main": "./index.cjs",
"module": "./index.js",
"types": "./index.d.cts",
"sideEffects": false
}
@@ -0,0 +1,32 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.z = void 0;
const z = __importStar(require("../v4/mini/external.cjs"));
exports.z = z;
__exportStar(require("../v4/mini/external.cjs"), exports);
@@ -0,0 +1,3 @@
import * as z from "../v4/mini/external.cjs";
export * from "../v4/mini/external.cjs";
export { z };
@@ -0,0 +1,3 @@
import * as z from "../v4/mini/external.js";
export * from "../v4/mini/external.js";
export { z };
@@ -0,0 +1,3 @@
import * as z from "../v4/mini/external.js";
export * from "../v4/mini/external.js";
export { z };
@@ -0,0 +1,7 @@
{
"type": "module",
"main": "./index.cjs",
"module": "./index.js",
"types": "./index.d.cts",
"sideEffects": false
}
@@ -0,0 +1,135 @@
{
"name": "zod",
"version": "4.4.3",
"type": "module",
"license": "MIT",
"author": "Colin McDonnell <zod@colinhacks.com>",
"description": "TypeScript-first schema declaration and validation library with static type inference",
"homepage": "https://zod.dev",
"llms": "https://zod.dev/llms.txt",
"llmsFull": "https://zod.dev/llms-full.txt",
"mcpServer": "https://mcp.inkeep.com/zod/mcp",
"funding": "https://github.com/sponsors/colinhacks",
"sideEffects": false,
"files": [
"src",
"**/*.js",
"**/*.mjs",
"**/*.cjs",
"**/*.d.ts",
"**/*.d.mts",
"**/*.d.cts",
"**/package.json"
],
"keywords": [
"typescript",
"schema",
"validation",
"type",
"inference"
],
"main": "./index.cjs",
"types": "./index.d.cts",
"module": "./index.js",
"zshy": {
"exports": {
"./package.json": "./package.json",
".": "./src/index.ts",
"./mini": "./src/mini/index.ts",
"./locales": "./src/locales/index.ts",
"./v3": "./src/v3/index.ts",
"./v4": "./src/v4/index.ts",
"./v4-mini": "./src/v4-mini/index.ts",
"./v4/mini": "./src/v4/mini/index.ts",
"./v4/core": "./src/v4/core/index.ts",
"./v4/locales": "./src/v4/locales/index.ts",
"./v4/locales/*": "./src/v4/locales/*"
},
"conditions": {
"@zod/source": "src"
}
},
"exports": {
"./package.json": "./package.json",
".": {
"@zod/source": "./src/index.ts",
"types": "./index.d.cts",
"import": "./index.js",
"require": "./index.cjs"
},
"./mini": {
"@zod/source": "./src/mini/index.ts",
"types": "./mini/index.d.cts",
"import": "./mini/index.js",
"require": "./mini/index.cjs"
},
"./locales": {
"@zod/source": "./src/locales/index.ts",
"types": "./locales/index.d.cts",
"import": "./locales/index.js",
"require": "./locales/index.cjs"
},
"./v3": {
"@zod/source": "./src/v3/index.ts",
"types": "./v3/index.d.cts",
"import": "./v3/index.js",
"require": "./v3/index.cjs"
},
"./v4": {
"@zod/source": "./src/v4/index.ts",
"types": "./v4/index.d.cts",
"import": "./v4/index.js",
"require": "./v4/index.cjs"
},
"./v4-mini": {
"@zod/source": "./src/v4-mini/index.ts",
"types": "./v4-mini/index.d.cts",
"import": "./v4-mini/index.js",
"require": "./v4-mini/index.cjs"
},
"./v4/mini": {
"@zod/source": "./src/v4/mini/index.ts",
"types": "./v4/mini/index.d.cts",
"import": "./v4/mini/index.js",
"require": "./v4/mini/index.cjs"
},
"./v4/core": {
"@zod/source": "./src/v4/core/index.ts",
"types": "./v4/core/index.d.cts",
"import": "./v4/core/index.js",
"require": "./v4/core/index.cjs"
},
"./v4/locales": {
"@zod/source": "./src/v4/locales/index.ts",
"types": "./v4/locales/index.d.cts",
"import": "./v4/locales/index.js",
"require": "./v4/locales/index.cjs"
},
"./v4/locales/*": {
"@zod/source": "./src/v4/locales/*",
"types": "./v4/locales/*",
"import": "./v4/locales/*",
"require": "./v4/locales/*"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/colinhacks/zod.git"
},
"bugs": {
"url": "https://github.com/colinhacks/zod/issues"
},
"support": {
"backing": {
"npm-funding": true
}
},
"scripts": {
"clean": "git clean -xdf . -e node_modules",
"build": "zshy --project tsconfig.build.json",
"postbuild": "tsx ../../scripts/write-stub-package-jsons.ts && pnpm biome check --write .",
"test:watch": "pnpm vitest",
"test": "pnpm vitest run",
"prepublishOnly": "tsx ../../scripts/check-versions.ts"
}
}
@@ -0,0 +1,4 @@
import * as z from "./v4/classic/external.js";
export * from "./v4/classic/external.js";
export { z };
export default z;
@@ -0,0 +1 @@
export * from "../v4/locales/index.js";
@@ -0,0 +1,3 @@
import * as z from "../v4/mini/external.js";
export * from "../v4/mini/external.js";
export { z };
@@ -0,0 +1,330 @@
import type { Primitive } from "./helpers/typeAliases.js";
import { util, type ZodParsedType } from "./helpers/util.js";
import type { TypeOf, ZodType } from "./index.js";
type allKeys<T> = T extends any ? keyof T : never;
export type inferFlattenedErrors<T extends ZodType<any, any, any>, U = string> = typeToFlattenedError<TypeOf<T>, U>;
export type typeToFlattenedError<T, U = string> = {
formErrors: U[];
fieldErrors: {
[P in allKeys<T>]?: U[];
};
};
export const ZodIssueCode = util.arrayToEnum([
"invalid_type",
"invalid_literal",
"custom",
"invalid_union",
"invalid_union_discriminator",
"invalid_enum_value",
"unrecognized_keys",
"invalid_arguments",
"invalid_return_type",
"invalid_date",
"invalid_string",
"too_small",
"too_big",
"invalid_intersection_types",
"not_multiple_of",
"not_finite",
]);
export type ZodIssueCode = keyof typeof ZodIssueCode;
export type ZodIssueBase = {
path: (string | number)[];
message?: string | undefined;
};
export interface ZodInvalidTypeIssue extends ZodIssueBase {
code: typeof ZodIssueCode.invalid_type;
expected: ZodParsedType;
received: ZodParsedType;
}
export interface ZodInvalidLiteralIssue extends ZodIssueBase {
code: typeof ZodIssueCode.invalid_literal;
expected: unknown;
received: unknown;
}
export interface ZodUnrecognizedKeysIssue extends ZodIssueBase {
code: typeof ZodIssueCode.unrecognized_keys;
keys: string[];
}
export interface ZodInvalidUnionIssue extends ZodIssueBase {
code: typeof ZodIssueCode.invalid_union;
unionErrors: ZodError[];
}
export interface ZodInvalidUnionDiscriminatorIssue extends ZodIssueBase {
code: typeof ZodIssueCode.invalid_union_discriminator;
options: Primitive[];
}
export interface ZodInvalidEnumValueIssue extends ZodIssueBase {
received: string | number;
code: typeof ZodIssueCode.invalid_enum_value;
options: (string | number)[];
}
export interface ZodInvalidArgumentsIssue extends ZodIssueBase {
code: typeof ZodIssueCode.invalid_arguments;
argumentsError: ZodError;
}
export interface ZodInvalidReturnTypeIssue extends ZodIssueBase {
code: typeof ZodIssueCode.invalid_return_type;
returnTypeError: ZodError;
}
export interface ZodInvalidDateIssue extends ZodIssueBase {
code: typeof ZodIssueCode.invalid_date;
}
export type StringValidation =
| "email"
| "url"
| "emoji"
| "uuid"
| "nanoid"
| "regex"
| "cuid"
| "cuid2"
| "ulid"
| "datetime"
| "date"
| "time"
| "duration"
| "ip"
| "cidr"
| "base64"
| "jwt"
| "base64url"
| { includes: string; position?: number | undefined }
| { startsWith: string }
| { endsWith: string };
export interface ZodInvalidStringIssue extends ZodIssueBase {
code: typeof ZodIssueCode.invalid_string;
validation: StringValidation;
}
export interface ZodTooSmallIssue extends ZodIssueBase {
code: typeof ZodIssueCode.too_small;
minimum: number | bigint;
inclusive: boolean;
exact?: boolean;
type: "array" | "string" | "number" | "set" | "date" | "bigint";
}
export interface ZodTooBigIssue extends ZodIssueBase {
code: typeof ZodIssueCode.too_big;
maximum: number | bigint;
inclusive: boolean;
exact?: boolean;
type: "array" | "string" | "number" | "set" | "date" | "bigint";
}
export interface ZodInvalidIntersectionTypesIssue extends ZodIssueBase {
code: typeof ZodIssueCode.invalid_intersection_types;
}
export interface ZodNotMultipleOfIssue extends ZodIssueBase {
code: typeof ZodIssueCode.not_multiple_of;
multipleOf: number | bigint;
}
export interface ZodNotFiniteIssue extends ZodIssueBase {
code: typeof ZodIssueCode.not_finite;
}
export interface ZodCustomIssue extends ZodIssueBase {
code: typeof ZodIssueCode.custom;
params?: { [k: string]: any };
}
export type DenormalizedError = { [k: string]: DenormalizedError | string[] };
export type ZodIssueOptionalMessage =
| ZodInvalidTypeIssue
| ZodInvalidLiteralIssue
| ZodUnrecognizedKeysIssue
| ZodInvalidUnionIssue
| ZodInvalidUnionDiscriminatorIssue
| ZodInvalidEnumValueIssue
| ZodInvalidArgumentsIssue
| ZodInvalidReturnTypeIssue
| ZodInvalidDateIssue
| ZodInvalidStringIssue
| ZodTooSmallIssue
| ZodTooBigIssue
| ZodInvalidIntersectionTypesIssue
| ZodNotMultipleOfIssue
| ZodNotFiniteIssue
| ZodCustomIssue;
export type ZodIssue = ZodIssueOptionalMessage & {
fatal?: boolean | undefined;
message: string;
};
export const quotelessJson = (obj: any) => {
const json = JSON.stringify(obj, null, 2);
return json.replace(/"([^"]+)":/g, "$1:");
};
type recursiveZodFormattedError<T> = T extends [any, ...any[]]
? { [K in keyof T]?: ZodFormattedError<T[K]> }
: T extends any[]
? { [k: number]: ZodFormattedError<T[number]> }
: T extends object
? { [K in keyof T]?: ZodFormattedError<T[K]> }
: unknown;
export type ZodFormattedError<T, U = string> = {
_errors: U[];
} & recursiveZodFormattedError<NonNullable<T>>;
export type inferFormattedError<T extends ZodType<any, any, any>, U = string> = ZodFormattedError<TypeOf<T>, U>;
export class ZodError<T = any> extends Error {
issues: ZodIssue[] = [];
get errors() {
return this.issues;
}
constructor(issues: ZodIssue[]) {
super();
const actualProto = new.target.prototype;
if (Object.setPrototypeOf) {
// eslint-disable-next-line ban/ban
Object.setPrototypeOf(this, actualProto);
} else {
(this as any).__proto__ = actualProto;
}
this.name = "ZodError";
this.issues = issues;
}
format(): ZodFormattedError<T>;
format<U>(mapper: (issue: ZodIssue) => U): ZodFormattedError<T, U>;
format(_mapper?: any) {
const mapper: (issue: ZodIssue) => any =
_mapper ||
function (issue: ZodIssue) {
return issue.message;
};
const fieldErrors: ZodFormattedError<T> = { _errors: [] } as any;
const processError = (error: ZodError) => {
for (const issue of error.issues) {
if (issue.code === "invalid_union") {
issue.unionErrors.map(processError);
} else if (issue.code === "invalid_return_type") {
processError(issue.returnTypeError);
} else if (issue.code === "invalid_arguments") {
processError(issue.argumentsError);
} else if (issue.path.length === 0) {
(fieldErrors as any)._errors.push(mapper(issue));
} else {
let curr: any = fieldErrors;
let i = 0;
while (i < issue.path.length) {
const el = issue.path[i]!;
const terminal = i === issue.path.length - 1;
if (!terminal) {
curr[el] = curr[el] || { _errors: [] };
// if (typeof el === "string") {
// curr[el] = curr[el] || { _errors: [] };
// } else if (typeof el === "number") {
// const errorArray: any = [];
// errorArray._errors = [];
// curr[el] = curr[el] || errorArray;
// }
} else {
curr[el] = curr[el] || { _errors: [] };
curr[el]._errors.push(mapper(issue));
}
curr = curr[el];
i++;
}
}
}
};
processError(this);
return fieldErrors;
}
static create = (issues: ZodIssue[]) => {
const error = new ZodError(issues);
return error;
};
static assert(value: unknown): asserts value is ZodError {
if (!(value instanceof ZodError)) {
throw new Error(`Not a ZodError: ${value}`);
}
}
override toString() {
return this.message;
}
override get message() {
return JSON.stringify(this.issues, util.jsonStringifyReplacer, 2);
}
get isEmpty(): boolean {
return this.issues.length === 0;
}
addIssue = (sub: ZodIssue) => {
this.issues = [...this.issues, sub];
};
addIssues = (subs: ZodIssue[] = []) => {
this.issues = [...this.issues, ...subs];
};
flatten(): typeToFlattenedError<T>;
flatten<U>(mapper?: (issue: ZodIssue) => U): typeToFlattenedError<T, U>;
flatten<U = string>(mapper: (issue: ZodIssue) => U = (issue: ZodIssue) => issue.message as any): any {
const fieldErrors: any = Object.create(null);
const formErrors: U[] = [];
for (const sub of this.issues) {
if (sub.path.length > 0) {
const firstEl = sub.path[0]!;
fieldErrors[firstEl] = fieldErrors[firstEl] || [];
fieldErrors[firstEl].push(mapper(sub));
} else {
formErrors.push(mapper(sub));
}
}
return { formErrors, fieldErrors };
}
get formErrors() {
return this.flatten();
}
}
type stripPath<T extends object> = T extends any ? util.OmitKeys<T, "path"> : never;
export type IssueData = stripPath<ZodIssueOptionalMessage> & {
path?: (string | number)[];
fatal?: boolean | undefined;
};
export type ErrorMapCtx = {
defaultError: string;
data: any;
};
export type ZodErrorMap = (issue: ZodIssueOptionalMessage, _ctx: ErrorMapCtx) => { message: string };
@@ -0,0 +1,58 @@
import Benchmark from "benchmark";
const datetimeValidationSuite = new Benchmark.Suite("datetime");
const DATA = "2021-01-01";
const MONTHS_31 = new Set([1, 3, 5, 7, 8, 10, 12]);
const MONTHS_30 = new Set([4, 6, 9, 11]);
const simpleDatetimeRegex = /^(\d{4})-(\d{2})-(\d{2})$/;
const datetimeRegexNoLeapYearValidation =
/^\d{4}-((0[13578]|10|12)-31|(0[13-9]|1[0-2])-30|(0[1-9]|1[0-2])-(0[1-9]|1\d|2\d))$/;
const datetimeRegexWithLeapYearValidation =
/^((\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\d|3[01])|(0[469]|11)-(0[1-9]|[12]\d|30)|(02)-(0[1-9]|1\d|2[0-8])))$/;
datetimeValidationSuite
.add("new Date()", () => {
return !Number.isNaN(new Date(DATA).getTime());
})
.add("regex (no validation)", () => {
return simpleDatetimeRegex.test(DATA);
})
.add("regex (no leap year)", () => {
return datetimeRegexNoLeapYearValidation.test(DATA);
})
.add("regex (w/ leap year)", () => {
return datetimeRegexWithLeapYearValidation.test(DATA);
})
.add("capture groups + code", () => {
const match = DATA.match(simpleDatetimeRegex);
if (!match) return false;
// Extract year, month, and day from the capture groups
const year = Number.parseInt(match[1], 10);
const month = Number.parseInt(match[2], 10); // month is 0-indexed in JavaScript Date, so subtract 1
const day = Number.parseInt(match[3], 10);
if (month === 2) {
if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
return day <= 29;
}
return day <= 28;
}
if (MONTHS_30.has(month)) {
return day <= 30;
}
if (MONTHS_31.has(month)) {
return day <= 31;
}
return false;
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${datetimeValidationSuite.name!}: ${e.target}`);
});
export default {
suites: [datetimeValidationSuite],
};
@@ -0,0 +1,80 @@
import Benchmark from "benchmark";
import { z } from "zod/v3";
const doubleSuite = new Benchmark.Suite("z.discriminatedUnion: double");
const manySuite = new Benchmark.Suite("z.discriminatedUnion: many");
const aSchema = z.object({
type: z.literal("a"),
});
const objA = {
type: "a",
};
const bSchema = z.object({
type: z.literal("b"),
});
const objB = {
type: "b",
};
const cSchema = z.object({
type: z.literal("c"),
});
const objC = {
type: "c",
};
const dSchema = z.object({
type: z.literal("d"),
});
const double = z.discriminatedUnion("type", [aSchema, bSchema]);
const many = z.discriminatedUnion("type", [aSchema, bSchema, cSchema, dSchema]);
doubleSuite
.add("valid: a", () => {
double.parse(objA);
})
.add("valid: b", () => {
double.parse(objB);
})
.add("invalid: null", () => {
try {
double.parse(null);
} catch (_err) {}
})
.add("invalid: wrong shape", () => {
try {
double.parse(objC);
} catch (_err) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${(doubleSuite as any).name}: ${e.target}`);
});
manySuite
.add("valid: a", () => {
many.parse(objA);
})
.add("valid: c", () => {
many.parse(objC);
})
.add("invalid: null", () => {
try {
many.parse(null);
} catch (_err) {}
})
.add("invalid: wrong shape", () => {
try {
many.parse({ type: "unknown" });
} catch (_err) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${(manySuite as any).name}: ${e.target}`);
});
export default {
suites: [doubleSuite, manySuite],
};
@@ -0,0 +1,59 @@
import type Benchmark from "benchmark";
import datetimeBenchmarks from "./datetime.js";
import discriminatedUnionBenchmarks from "./discriminatedUnion.js";
import ipv4Benchmarks from "./ipv4.js";
import objectBenchmarks from "./object.js";
import primitiveBenchmarks from "./primitives.js";
import realworld from "./realworld.js";
import stringBenchmarks from "./string.js";
import unionBenchmarks from "./union.js";
const argv = process.argv.slice(2);
let suites: Benchmark.Suite[] = [];
if (!argv.length) {
suites = [
...realworld.suites,
...primitiveBenchmarks.suites,
...stringBenchmarks.suites,
...objectBenchmarks.suites,
...unionBenchmarks.suites,
...discriminatedUnionBenchmarks.suites,
];
} else {
if (argv.includes("--realworld")) {
suites.push(...realworld.suites);
}
if (argv.includes("--primitives")) {
suites.push(...primitiveBenchmarks.suites);
}
if (argv.includes("--string")) {
suites.push(...stringBenchmarks.suites);
}
if (argv.includes("--object")) {
suites.push(...objectBenchmarks.suites);
}
if (argv.includes("--union")) {
suites.push(...unionBenchmarks.suites);
}
if (argv.includes("--discriminatedUnion")) {
suites.push(...datetimeBenchmarks.suites);
}
if (argv.includes("--datetime")) {
suites.push(...datetimeBenchmarks.suites);
}
if (argv.includes("--ipv4")) {
suites.push(...ipv4Benchmarks.suites);
}
}
for (const suite of suites) {
suite.run({});
}
// exit on Ctrl-C
process.on("SIGINT", function () {
console.log("Exiting...");
process.exit();
});
@@ -0,0 +1,57 @@
import Benchmark from "benchmark";
const suite = new Benchmark.Suite("ipv4");
const DATA = "127.0.0.1";
const ipv4RegexA =
/^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/;
const ipv4RegexB =
/^(?:(?:(?=(25[0-5]))\1|(?=(2[0-4][0-9]))\2|(?=(1[0-9]{2}))\3|(?=([0-9]{1,2}))\4)\.){3}(?:(?=(25[0-5]))\5|(?=(2[0-4][0-9]))\6|(?=(1[0-9]{2}))\7|(?=([0-9]{1,2}))\8)$/;
const ipv4RegexC = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
const ipv4RegexD = /^(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/;
const ipv4RegexE = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.){3}(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)$/;
const ipv4RegexF = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/;
const ipv4RegexG = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/;
const ipv4RegexH = /^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$/;
const ipv4RegexI =
/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
suite
.add("A", () => {
return ipv4RegexA.test(DATA);
})
.add("B", () => {
return ipv4RegexB.test(DATA);
})
.add("C", () => {
return ipv4RegexC.test(DATA);
})
.add("D", () => {
return ipv4RegexD.test(DATA);
})
.add("E", () => {
return ipv4RegexE.test(DATA);
})
.add("F", () => {
return ipv4RegexF.test(DATA);
})
.add("G", () => {
return ipv4RegexG.test(DATA);
})
.add("H", () => {
return ipv4RegexH.test(DATA);
})
.add("I", () => {
return ipv4RegexI.test(DATA);
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${suite.name!}: ${e.target}`);
});
export default {
suites: [suite],
};
if (require.main === module) {
suite.run();
}
@@ -0,0 +1,69 @@
import Benchmark from "benchmark";
import { z } from "zod/v3";
const emptySuite = new Benchmark.Suite("z.object: empty");
const shortSuite = new Benchmark.Suite("z.object: short");
const longSuite = new Benchmark.Suite("z.object: long");
const empty = z.object({});
const short = z.object({
string: z.string(),
});
const long = z.object({
string: z.string(),
number: z.number(),
boolean: z.boolean(),
});
emptySuite
.add("valid", () => {
empty.parse({});
})
.add("valid: extra keys", () => {
empty.parse({ string: "string" });
})
.add("invalid: null", () => {
try {
empty.parse(null);
} catch (_err) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${(emptySuite as any).name}: ${e.target}`);
});
shortSuite
.add("valid", () => {
short.parse({ string: "string" });
})
.add("valid: extra keys", () => {
short.parse({ string: "string", number: 42 });
})
.add("invalid: null", () => {
try {
short.parse(null);
} catch (_err) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${(shortSuite as any).name}: ${e.target}`);
});
longSuite
.add("valid", () => {
long.parse({ string: "string", number: 42, boolean: true });
})
.add("valid: extra keys", () => {
long.parse({ string: "string", number: 42, boolean: true, list: [] });
})
.add("invalid: null", () => {
try {
long.parse(null);
} catch (_err) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${(longSuite as any).name}: ${e.target}`);
});
export default {
suites: [emptySuite, shortSuite, longSuite],
};
@@ -0,0 +1,162 @@
import Benchmark from "benchmark";
import { z } from "zod/v3";
import { Mocker } from "../tests/Mocker.js";
const val = new Mocker();
const enumSuite = new Benchmark.Suite("z.enum");
const enumSchema = z.enum(["a", "b", "c"]);
enumSuite
.add("valid", () => {
enumSchema.parse("a");
})
.add("invalid", () => {
try {
enumSchema.parse("x");
} catch (_e: any) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`z.enum: ${e.target}`);
});
const longEnumSuite = new Benchmark.Suite("long z.enum");
const longEnumSchema = z.enum([
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
]);
longEnumSuite
.add("valid", () => {
longEnumSchema.parse("five");
})
.add("invalid", () => {
try {
longEnumSchema.parse("invalid");
} catch (_e: any) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`long z.enum: ${e.target}`);
});
const undefinedSuite = new Benchmark.Suite("z.undefined");
const undefinedSchema = z.undefined();
undefinedSuite
.add("valid", () => {
undefinedSchema.parse(undefined);
})
.add("invalid", () => {
try {
undefinedSchema.parse(1);
} catch (_e: any) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`z.undefined: ${e.target}`);
});
const literalSuite = new Benchmark.Suite("z.literal");
const short = "short";
const bad = "bad";
const literalSchema = z.literal("short");
literalSuite
.add("valid", () => {
literalSchema.parse(short);
})
.add("invalid", () => {
try {
literalSchema.parse(bad);
} catch (_e: any) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`z.literal: ${e.target}`);
});
const numberSuite = new Benchmark.Suite("z.number");
const numberSchema = z.number().int();
numberSuite
.add("valid", () => {
numberSchema.parse(1);
})
.add("invalid type", () => {
try {
numberSchema.parse("bad");
} catch (_e: any) {}
})
.add("invalid number", () => {
try {
numberSchema.parse(0.5);
} catch (_e: any) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`z.number: ${e.target}`);
});
const dateSuite = new Benchmark.Suite("z.date");
const plainDate = z.date();
const minMaxDate = z.date().min(new Date("2021-01-01")).max(new Date("2030-01-01"));
dateSuite
.add("valid", () => {
plainDate.parse(new Date());
})
.add("invalid", () => {
try {
plainDate.parse(1);
} catch (_e: any) {}
})
.add("valid min and max", () => {
minMaxDate.parse(new Date("2023-01-01"));
})
.add("invalid min", () => {
try {
minMaxDate.parse(new Date("2019-01-01"));
} catch (_e: any) {}
})
.add("invalid max", () => {
try {
minMaxDate.parse(new Date("2031-01-01"));
} catch (_e: any) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`z.date: ${e.target}`);
});
const symbolSuite = new Benchmark.Suite("z.symbol");
const symbolSchema = z.symbol();
symbolSuite
.add("valid", () => {
symbolSchema.parse(val.symbol);
})
.add("invalid", () => {
try {
symbolSchema.parse(1);
} catch (_e: any) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`z.symbol: ${e.target}`);
});
export default {
suites: [enumSuite, longEnumSuite, undefinedSuite, literalSuite, numberSuite, dateSuite, symbolSuite],
};
@@ -0,0 +1,63 @@
import Benchmark from "benchmark";
import { z } from "zod/v3";
const shortSuite = new Benchmark.Suite("realworld");
const People = z.array(
z.object({
type: z.literal("person"),
hair: z.enum(["blue", "brown"]),
active: z.boolean(),
name: z.string(),
age: z.number().int(),
hobbies: z.array(z.string()),
address: z.object({
street: z.string(),
zip: z.string(),
country: z.string(),
}),
})
);
let i = 0;
function num() {
return ++i;
}
function str() {
return (++i % 100).toString(16);
}
function array<T>(fn: () => T): T[] {
return Array.from({ length: ++i % 10 }, () => fn());
}
const people = Array.from({ length: 100 }, () => {
return {
type: "person",
hair: i % 2 ? "blue" : "brown",
active: !!(i % 2),
name: str(),
age: num(),
hobbies: array(str),
address: {
street: str(),
zip: str(),
country: str(),
},
};
});
shortSuite
.add("valid", () => {
People.parse(people);
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${(shortSuite as any).name}: ${e.target}`);
});
export default {
suites: [shortSuite],
};
@@ -0,0 +1,55 @@
import Benchmark from "benchmark";
import { z } from "zod/v3";
const SUITE_NAME = "z.string";
const suite = new Benchmark.Suite(SUITE_NAME);
const empty = "";
const short = "short";
const long = "long".repeat(256);
const manual = (str: unknown) => {
if (typeof str !== "string") {
throw new Error("Not a string");
}
return str;
};
const stringSchema = z.string();
const optionalStringSchema = z.string().optional();
const optionalNullableStringSchema = z.string().optional().nullable();
suite
.add("empty string", () => {
stringSchema.parse(empty);
})
.add("short string", () => {
stringSchema.parse(short);
})
.add("long string", () => {
stringSchema.parse(long);
})
.add("optional string", () => {
optionalStringSchema.parse(long);
})
.add("nullable string", () => {
optionalNullableStringSchema.parse(long);
})
.add("nullable (null) string", () => {
optionalNullableStringSchema.parse(null);
})
.add("invalid: null", () => {
try {
stringSchema.parse(null);
} catch (_err) {}
})
.add("manual parser: long", () => {
manual(long);
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${SUITE_NAME}: ${e.target}`);
});
export default {
suites: [suite],
};
@@ -0,0 +1,80 @@
import Benchmark from "benchmark";
import { z } from "zod/v3";
const doubleSuite = new Benchmark.Suite("z.union: double");
const manySuite = new Benchmark.Suite("z.union: many");
const aSchema = z.object({
type: z.literal("a"),
});
const objA = {
type: "a",
};
const bSchema = z.object({
type: z.literal("b"),
});
const objB = {
type: "b",
};
const cSchema = z.object({
type: z.literal("c"),
});
const objC = {
type: "c",
};
const dSchema = z.object({
type: z.literal("d"),
});
const double = z.union([aSchema, bSchema]);
const many = z.union([aSchema, bSchema, cSchema, dSchema]);
doubleSuite
.add("valid: a", () => {
double.parse(objA);
})
.add("valid: b", () => {
double.parse(objB);
})
.add("invalid: null", () => {
try {
double.parse(null);
} catch (_err) {}
})
.add("invalid: wrong shape", () => {
try {
double.parse(objC);
} catch (_err) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${(doubleSuite as any).name}: ${e.target}`);
});
manySuite
.add("valid: a", () => {
many.parse(objA);
})
.add("valid: c", () => {
many.parse(objC);
})
.add("invalid: null", () => {
try {
many.parse(null);
} catch (_err) {}
})
.add("invalid: wrong shape", () => {
try {
many.parse({ type: "unknown" });
} catch (_err) {}
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${(manySuite as any).name}: ${e.target}`);
});
export default {
suites: [doubleSuite, manySuite],
};
@@ -0,0 +1,13 @@
import type { ZodErrorMap } from "./ZodError.js";
import defaultErrorMap from "./locales/en.js";
let overrideErrorMap = defaultErrorMap;
export { defaultErrorMap };
export function setErrorMap(map: ZodErrorMap) {
overrideErrorMap = map;
}
export function getErrorMap() {
return overrideErrorMap;
}
@@ -0,0 +1,6 @@
export * from "./errors.js";
export * from "./helpers/parseUtil.js";
export * from "./helpers/typeAliases.js";
export * from "./helpers/util.js";
export * from "./types.js";
export * from "./ZodError.js";
@@ -0,0 +1,17 @@
export namespace enumUtil {
type UnionToIntersectionFn<T> = (T extends unknown ? (k: () => T) => void : never) extends (
k: infer Intersection
) => void
? Intersection
: never;
type GetUnionLast<T> = UnionToIntersectionFn<T> extends () => infer Last ? Last : never;
type UnionToTuple<T, Tuple extends unknown[] = []> = [T] extends [never]
? Tuple
: UnionToTuple<Exclude<T, GetUnionLast<T>>, [GetUnionLast<T>, ...Tuple]>;
type CastToStringTuple<T> = T extends [string, ...string[]] ? T : never;
export type UnionToTupleString<T> = CastToStringTuple<UnionToTuple<T>>;
}
@@ -0,0 +1,8 @@
export namespace errorUtil {
export type ErrMessage = string | { message?: string | undefined };
export const errToObj = (message?: ErrMessage): { message?: string | undefined } =>
typeof message === "string" ? { message } : message || {};
// biome-ignore lint:
export const toString = (message?: ErrMessage): string | undefined =>
typeof message === "string" ? message : message?.message;
}
@@ -0,0 +1,176 @@
import type { IssueData, ZodErrorMap, ZodIssue } from "../ZodError.js";
import { getErrorMap } from "../errors.js";
import defaultErrorMap from "../locales/en.js";
import type { ZodParsedType } from "./util.js";
export const makeIssue = (params: {
data: any;
path: (string | number)[];
errorMaps: ZodErrorMap[];
issueData: IssueData;
}): ZodIssue => {
const { data, path, errorMaps, issueData } = params;
const fullPath = [...path, ...(issueData.path || [])];
const fullIssue = {
...issueData,
path: fullPath,
};
if (issueData.message !== undefined) {
return {
...issueData,
path: fullPath,
message: issueData.message,
};
}
let errorMessage = "";
const maps = errorMaps
.filter((m) => !!m)
.slice()
.reverse();
for (const map of maps) {
errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message;
}
return {
...issueData,
path: fullPath,
message: errorMessage,
};
};
export type ParseParams = {
path: (string | number)[];
errorMap: ZodErrorMap;
async: boolean;
};
export type ParsePathComponent = string | number;
export type ParsePath = ParsePathComponent[];
export const EMPTY_PATH: ParsePath = [];
export interface ParseContext {
readonly common: {
readonly issues: ZodIssue[];
readonly contextualErrorMap?: ZodErrorMap | undefined;
readonly async: boolean;
};
readonly path: ParsePath;
readonly schemaErrorMap?: ZodErrorMap | undefined;
readonly parent: ParseContext | null;
readonly data: any;
readonly parsedType: ZodParsedType;
}
export type ParseInput = {
data: any;
path: (string | number)[];
parent: ParseContext;
};
export function addIssueToContext(ctx: ParseContext, issueData: IssueData): void {
const overrideMap = getErrorMap();
const issue = makeIssue({
issueData: issueData,
data: ctx.data,
path: ctx.path,
errorMaps: [
ctx.common.contextualErrorMap, // contextual error map is first priority
ctx.schemaErrorMap, // then schema-bound map if available
overrideMap, // then global override map
overrideMap === defaultErrorMap ? undefined : defaultErrorMap, // then global default map
].filter((x) => !!x),
});
ctx.common.issues.push(issue);
}
export type ObjectPair = {
key: SyncParseReturnType<any>;
value: SyncParseReturnType<any>;
};
export class ParseStatus {
value: "aborted" | "dirty" | "valid" = "valid";
dirty(): void {
if (this.value === "valid") this.value = "dirty";
}
abort(): void {
if (this.value !== "aborted") this.value = "aborted";
}
static mergeArray(status: ParseStatus, results: SyncParseReturnType<any>[]): SyncParseReturnType {
const arrayValue: any[] = [];
for (const s of results) {
if (s.status === "aborted") return INVALID;
if (s.status === "dirty") status.dirty();
arrayValue.push(s.value);
}
return { status: status.value, value: arrayValue };
}
static async mergeObjectAsync(
status: ParseStatus,
pairs: { key: ParseReturnType<any>; value: ParseReturnType<any> }[]
): Promise<SyncParseReturnType<any>> {
const syncPairs: ObjectPair[] = [];
for (const pair of pairs) {
const key = await pair.key;
const value = await pair.value;
syncPairs.push({
key,
value,
});
}
return ParseStatus.mergeObjectSync(status, syncPairs);
}
static mergeObjectSync(
status: ParseStatus,
pairs: {
key: SyncParseReturnType<any>;
value: SyncParseReturnType<any>;
alwaysSet?: boolean;
}[]
): SyncParseReturnType {
const finalObject: any = {};
for (const pair of pairs) {
const { key, value } = pair;
if (key.status === "aborted") return INVALID;
if (value.status === "aborted") return INVALID;
if (key.status === "dirty") status.dirty();
if (value.status === "dirty") status.dirty();
if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) {
finalObject[key.value] = value.value;
}
}
return { status: status.value, value: finalObject };
}
}
export interface ParseResult {
status: "aborted" | "dirty" | "valid";
data: any;
}
export type INVALID = { status: "aborted" };
export const INVALID: INVALID = Object.freeze({
status: "aborted",
});
export type DIRTY<T> = { status: "dirty"; value: T };
export const DIRTY = <T>(value: T): DIRTY<T> => ({ status: "dirty", value });
export type OK<T> = { status: "valid"; value: T };
export const OK = <T>(value: T): OK<T> => ({ status: "valid", value });
export type SyncParseReturnType<T = any> = OK<T> | DIRTY<T> | INVALID;
export type AsyncParseReturnType<T> = Promise<SyncParseReturnType<T>>;
export type ParseReturnType<T> = SyncParseReturnType<T> | AsyncParseReturnType<T>;
export const isAborted = (x: ParseReturnType<any>): x is INVALID => (x as any).status === "aborted";
export const isDirty = <T>(x: ParseReturnType<T>): x is OK<T> | DIRTY<T> => (x as any).status === "dirty";
export const isValid = <T>(x: ParseReturnType<T>): x is OK<T> => (x as any).status === "valid";
export const isAsync = <T>(x: ParseReturnType<T>): x is AsyncParseReturnType<T> =>
typeof Promise !== "undefined" && x instanceof Promise;
@@ -0,0 +1,34 @@
import type {
ZodArray,
ZodNullable,
ZodObject,
ZodOptional,
ZodRawShape,
ZodTuple,
ZodTupleItems,
ZodTypeAny,
} from "../types.js";
export namespace partialUtil {
export type DeepPartial<T extends ZodTypeAny> = T extends ZodObject<ZodRawShape>
? ZodObject<
{ [k in keyof T["shape"]]: ZodOptional<DeepPartial<T["shape"][k]>> },
T["_def"]["unknownKeys"],
T["_def"]["catchall"]
>
: T extends ZodArray<infer Type, infer Card>
? ZodArray<DeepPartial<Type>, Card>
: T extends ZodOptional<infer Type>
? ZodOptional<DeepPartial<Type>>
: T extends ZodNullable<infer Type>
? ZodNullable<DeepPartial<Type>>
: T extends ZodTuple<infer Items>
? {
[k in keyof Items]: Items[k] extends ZodTypeAny ? DeepPartial<Items[k]> : never;
} extends infer PI
? PI extends ZodTupleItems
? ZodTuple<PI>
: never
: never
: T;
}
@@ -0,0 +1,2 @@
export type Primitive = string | number | symbol | bigint | boolean | null | undefined;
export type Scalars = Primitive | Primitive[];
@@ -0,0 +1,224 @@
export namespace util {
type AssertEqual<T, U> = (<V>() => V extends T ? 1 : 2) extends <V>() => V extends U ? 1 : 2 ? true : false;
export type isAny<T> = 0 extends 1 & T ? true : false;
export const assertEqual = <A, B>(_: AssertEqual<A, B>): void => {};
export function assertIs<T>(_arg: T): void {}
export function assertNever(_x: never): never {
throw new Error();
}
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
export type OmitKeys<T, K extends string> = Pick<T, Exclude<keyof T, K>>;
export type MakePartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
export type Exactly<T, X> = T & Record<Exclude<keyof X, keyof T>, never>;
export type InexactPartial<T> = { [k in keyof T]?: T[k] | undefined };
export const arrayToEnum = <T extends string, U extends [T, ...T[]]>(items: U): { [k in U[number]]: k } => {
const obj: any = {};
for (const item of items) {
obj[item] = item;
}
return obj;
};
export const getValidEnumValues = (obj: any): any[] => {
const validKeys = objectKeys(obj).filter((k: any) => typeof obj[obj[k]] !== "number");
const filtered: any = {};
for (const k of validKeys) {
filtered[k] = obj[k];
}
return objectValues(filtered);
};
export const objectValues = (obj: any): any[] => {
return objectKeys(obj).map(function (e) {
return obj[e];
});
};
export const objectKeys: ObjectConstructor["keys"] =
typeof Object.keys === "function" // eslint-disable-line ban/ban
? (obj: any) => Object.keys(obj) // eslint-disable-line ban/ban
: (object: any) => {
const keys = [];
for (const key in object) {
if (Object.prototype.hasOwnProperty.call(object, key)) {
keys.push(key);
}
}
return keys;
};
export const find = <T>(arr: T[], checker: (arg: T) => any): T | undefined => {
for (const item of arr) {
if (checker(item)) return item;
}
return undefined;
};
export type identity<T> = objectUtil.identity<T>;
export type flatten<T> = objectUtil.flatten<T>;
export type noUndefined<T> = T extends undefined ? never : T;
export const isInteger: NumberConstructor["isInteger"] =
typeof Number.isInteger === "function"
? (val) => Number.isInteger(val) // eslint-disable-line ban/ban
: (val) => typeof val === "number" && Number.isFinite(val) && Math.floor(val) === val;
export function joinValues<T extends any[]>(array: T, separator = " | "): string {
return array.map((val) => (typeof val === "string" ? `'${val}'` : val)).join(separator);
}
export const jsonStringifyReplacer = (_: string, value: any): any => {
if (typeof value === "bigint") {
return value.toString();
}
return value;
};
}
export namespace objectUtil {
export type MergeShapes<U, V> =
// fast path when there is no keys overlap
keyof U & keyof V extends never
? U & V
: {
[k in Exclude<keyof U, keyof V>]: U[k];
} & V;
type optionalKeys<T extends object> = {
[k in keyof T]: undefined extends T[k] ? k : never;
}[keyof T];
type requiredKeys<T extends object> = {
[k in keyof T]: undefined extends T[k] ? never : k;
}[keyof T];
export type addQuestionMarks<T extends object, _O = any> = {
[K in requiredKeys<T>]: T[K];
} & {
[K in optionalKeys<T>]?: T[K];
} & { [k in keyof T]?: unknown };
export type identity<T> = T;
export type flatten<T> = identity<{ [k in keyof T]: T[k] }>;
export type noNeverKeys<T> = {
[k in keyof T]: [T[k]] extends [never] ? never : k;
}[keyof T];
export type noNever<T> = identity<{
[k in noNeverKeys<T>]: k extends keyof T ? T[k] : never;
}>;
export const mergeShapes = <U, T>(first: U, second: T): T & U => {
return {
...first,
...second, // second overwrites first
};
};
export type extendShape<A extends object, B extends object> = keyof A & keyof B extends never // fast path when there is no keys overlap
? A & B
: {
[K in keyof A as K extends keyof B ? never : K]: A[K];
} & {
[K in keyof B]: B[K];
};
}
export const ZodParsedType: {
string: "string";
nan: "nan";
number: "number";
integer: "integer";
float: "float";
boolean: "boolean";
date: "date";
bigint: "bigint";
symbol: "symbol";
function: "function";
undefined: "undefined";
null: "null";
array: "array";
object: "object";
unknown: "unknown";
promise: "promise";
void: "void";
never: "never";
map: "map";
set: "set";
} = util.arrayToEnum([
"string",
"nan",
"number",
"integer",
"float",
"boolean",
"date",
"bigint",
"symbol",
"function",
"undefined",
"null",
"array",
"object",
"unknown",
"promise",
"void",
"never",
"map",
"set",
]);
export type ZodParsedType = keyof typeof ZodParsedType;
export const getParsedType = (data: any): ZodParsedType => {
const t = typeof data;
switch (t) {
case "undefined":
return ZodParsedType.undefined;
case "string":
return ZodParsedType.string;
case "number":
return Number.isNaN(data) ? ZodParsedType.nan : ZodParsedType.number;
case "boolean":
return ZodParsedType.boolean;
case "function":
return ZodParsedType.function;
case "bigint":
return ZodParsedType.bigint;
case "symbol":
return ZodParsedType.symbol;
case "object":
if (Array.isArray(data)) {
return ZodParsedType.array;
}
if (data === null) {
return ZodParsedType.null;
}
if (data.then && typeof data.then === "function" && data.catch && typeof data.catch === "function") {
return ZodParsedType.promise;
}
if (typeof Map !== "undefined" && data instanceof Map) {
return ZodParsedType.map;
}
if (typeof Set !== "undefined" && data instanceof Set) {
return ZodParsedType.set;
}
if (typeof Date !== "undefined" && data instanceof Date) {
return ZodParsedType.date;
}
return ZodParsedType.object;
default:
return ZodParsedType.unknown;
}
};
@@ -0,0 +1,4 @@
import * as z from "./external.js";
export * from "./external.js";
export { z };
export default z;
@@ -0,0 +1,124 @@
import { type ZodErrorMap, ZodIssueCode } from "../ZodError.js";
import { util, ZodParsedType } from "../helpers/util.js";
const errorMap: ZodErrorMap = (issue, _ctx) => {
let message: string;
switch (issue.code) {
case ZodIssueCode.invalid_type:
if (issue.received === ZodParsedType.undefined) {
message = "Required";
} else {
message = `Expected ${issue.expected}, received ${issue.received}`;
}
break;
case ZodIssueCode.invalid_literal:
message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util.jsonStringifyReplacer)}`;
break;
case ZodIssueCode.unrecognized_keys:
message = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, ", ")}`;
break;
case ZodIssueCode.invalid_union:
message = `Invalid input`;
break;
case ZodIssueCode.invalid_union_discriminator:
message = `Invalid discriminator value. Expected ${util.joinValues(issue.options)}`;
break;
case ZodIssueCode.invalid_enum_value:
message = `Invalid enum value. Expected ${util.joinValues(issue.options)}, received '${issue.received}'`;
break;
case ZodIssueCode.invalid_arguments:
message = `Invalid function arguments`;
break;
case ZodIssueCode.invalid_return_type:
message = `Invalid function return type`;
break;
case ZodIssueCode.invalid_date:
message = `Invalid date`;
break;
case ZodIssueCode.invalid_string:
if (typeof issue.validation === "object") {
if ("includes" in issue.validation) {
message = `Invalid input: must include "${issue.validation.includes}"`;
if (typeof issue.validation.position === "number") {
message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`;
}
} else if ("startsWith" in issue.validation) {
message = `Invalid input: must start with "${issue.validation.startsWith}"`;
} else if ("endsWith" in issue.validation) {
message = `Invalid input: must end with "${issue.validation.endsWith}"`;
} else {
util.assertNever(issue.validation);
}
} else if (issue.validation !== "regex") {
message = `Invalid ${issue.validation}`;
} else {
message = "Invalid";
}
break;
case ZodIssueCode.too_small:
if (issue.type === "array")
message = `Array must contain ${
issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`
} ${issue.minimum} element(s)`;
else if (issue.type === "string")
message = `String must contain ${
issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`
} ${issue.minimum} character(s)`;
else if (issue.type === "number")
message = `Number must be ${
issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `
}${issue.minimum}`;
else if (issue.type === "bigint")
message = `Number must be ${
issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `
}${issue.minimum}`;
else if (issue.type === "date")
message = `Date must be ${
issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `
}${new Date(Number(issue.minimum))}`;
else message = "Invalid input";
break;
case ZodIssueCode.too_big:
if (issue.type === "array")
message = `Array must contain ${
issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`
} ${issue.maximum} element(s)`;
else if (issue.type === "string")
message = `String must contain ${
issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`
} ${issue.maximum} character(s)`;
else if (issue.type === "number")
message = `Number must be ${
issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`
} ${issue.maximum}`;
else if (issue.type === "bigint")
message = `BigInt must be ${
issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`
} ${issue.maximum}`;
else if (issue.type === "date")
message = `Date must be ${
issue.exact ? `exactly` : issue.inclusive ? `smaller than or equal to` : `smaller than`
} ${new Date(Number(issue.maximum))}`;
else message = "Invalid input";
break;
case ZodIssueCode.custom:
message = `Invalid input`;
break;
case ZodIssueCode.invalid_intersection_types:
message = `Intersection results could not be merged`;
break;
case ZodIssueCode.not_multiple_of:
message = `Number must be a multiple of ${issue.multipleOf}`;
break;
case ZodIssueCode.not_finite:
message = "Number must be finite";
break;
default:
message = _ctx.defaultError;
util.assertNever(issue);
}
return { message };
};
export default errorMap;
@@ -0,0 +1,113 @@
/**
* The Standard Schema interface.
*/
export type StandardSchemaV1<Input = unknown, Output = Input> = {
/**
* The Standard Schema properties.
*/
readonly "~standard": StandardSchemaV1.Props<Input, Output>;
};
export declare namespace StandardSchemaV1 {
/**
* The Standard Schema properties interface.
*/
export interface Props<Input = unknown, Output = Input> {
/**
* The version number of the standard.
*/
readonly version: 1;
/**
* The vendor name of the schema library.
*/
readonly vendor: string;
/**
* Validates unknown input values.
*/
readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
/**
* Inferred types associated with the schema.
*/
readonly types?: Types<Input, Output> | undefined;
}
/**
* The result interface of the validate function.
*/
export type Result<Output> = SuccessResult<Output> | FailureResult;
/**
* The result interface if validation succeeds.
*/
export interface SuccessResult<Output> {
/**
* The typed output value.
*/
readonly value: Output;
/**
* The non-existent issues.
*/
readonly issues?: undefined;
}
/**
* The result interface if validation fails.
*/
export interface FailureResult {
/**
* The issues of failed validation.
*/
readonly issues: ReadonlyArray<Issue>;
}
/**
* The issue interface of the failure output.
*/
export interface Issue {
/**
* The error message of the issue.
*/
readonly message: string;
/**
* The path of the issue, if any.
*/
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
}
/**
* The path segment interface of the issue.
*/
export interface PathSegment {
/**
* The key representing a path segment.
*/
readonly key: PropertyKey;
}
/**
* The Standard Schema types interface.
*/
export interface Types<Input = unknown, Output = Input> {
/**
* The input type of the schema.
*/
readonly input: Input;
/**
* The output type of the schema.
*/
readonly output: Output;
}
/**
* Infers the input type of a Standard Schema.
*/
export type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["input"];
/**
* Infers the output type of a Standard Schema.
*/
export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["output"];
// biome-ignore lint/complexity/noUselessEmptyExport: needed for granular visibility control of TS namespace
export {};
}
@@ -0,0 +1,54 @@
function getRandomInt(max: number) {
return Math.floor(Math.random() * Math.floor(max));
}
const testSymbol = Symbol("test");
export class Mocker {
pick = (...args: any[]): any => {
return args[getRandomInt(args.length)];
};
get string(): string {
return Math.random().toString(36).substring(7);
}
get number(): number {
return Math.random() * 100;
}
get bigint(): bigint {
return BigInt(Math.floor(Math.random() * 10000));
}
get boolean(): boolean {
return Math.random() < 0.5;
}
get date(): Date {
return new Date(Math.floor(Date.now() * Math.random()));
}
get symbol(): symbol {
return testSymbol;
}
get null(): null {
return null;
}
get undefined(): undefined {
return undefined;
}
get stringOptional(): string | undefined {
return this.pick(this.string, this.undefined);
}
get stringNullable(): string | null {
return this.pick(this.string, this.null);
}
get numberOptional(): number | undefined {
return this.pick(this.number, this.undefined);
}
get numberNullable(): number | null {
return this.pick(this.number, this.null);
}
get booleanOptional(): boolean | undefined {
return this.pick(this.boolean, this.undefined);
}
get booleanNullable(): boolean | null {
return this.pick(this.boolean, this.null);
}
}
@@ -0,0 +1,157 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
import { util } from "../helpers/util.js";
const Test = z.object({
f1: z.number(),
f2: z.string().optional(),
f3: z.string().nullable(),
f4: z.array(z.object({ t: z.union([z.string(), z.boolean()]) })),
});
type TestFlattenedErrors = z.inferFlattenedErrors<typeof Test, { message: string; code: number }>;
type TestFormErrors = z.inferFlattenedErrors<typeof Test>;
test("default flattened errors type inference", () => {
type TestTypeErrors = {
formErrors: string[];
fieldErrors: { [P in keyof z.TypeOf<typeof Test>]?: string[] };
};
util.assertEqual<z.inferFlattenedErrors<typeof Test>, TestTypeErrors>(true);
util.assertEqual<z.inferFlattenedErrors<typeof Test, { message: string }>, TestTypeErrors>(false);
});
test("custom flattened errors type inference", () => {
type ErrorType = { message: string; code: number };
type TestTypeErrors = {
formErrors: ErrorType[];
fieldErrors: {
[P in keyof z.TypeOf<typeof Test>]?: ErrorType[];
};
};
util.assertEqual<z.inferFlattenedErrors<typeof Test>, TestTypeErrors>(false);
util.assertEqual<z.inferFlattenedErrors<typeof Test, { message: string; code: number }>, TestTypeErrors>(true);
util.assertEqual<z.inferFlattenedErrors<typeof Test, { message: string }>, TestTypeErrors>(false);
});
test("form errors type inference", () => {
type TestTypeErrors = {
formErrors: string[];
fieldErrors: { [P in keyof z.TypeOf<typeof Test>]?: string[] };
};
util.assertEqual<z.inferFlattenedErrors<typeof Test>, TestTypeErrors>(true);
});
test(".flatten() type assertion", () => {
const parsed = Test.safeParse({}) as z.SafeParseError<void>;
const validFlattenedErrors: TestFlattenedErrors = parsed.error.flatten(() => ({ message: "", code: 0 }));
// @ts-expect-error should fail assertion between `TestFlattenedErrors` and unmapped `flatten()`.
const invalidFlattenedErrors: TestFlattenedErrors = parsed.error.flatten();
const validFormErrors: TestFormErrors = parsed.error.flatten();
// @ts-expect-error should fail assertion between `TestFormErrors` and mapped `flatten()`.
const invalidFormErrors: TestFormErrors = parsed.error.flatten(() => ({
message: "string",
code: 0,
}));
[validFlattenedErrors, invalidFlattenedErrors, validFormErrors, invalidFormErrors];
});
test(".formErrors type assertion", () => {
const parsed = Test.safeParse({}) as z.SafeParseError<void>;
const validFormErrors: TestFormErrors = parsed.error.formErrors;
// @ts-expect-error should fail assertion between `TestFlattenedErrors` and `.formErrors`.
const invalidFlattenedErrors: TestFlattenedErrors = parsed.error.formErrors;
[validFormErrors, invalidFlattenedErrors];
});
test("all errors", () => {
const propertySchema = z.string();
const schema = z
.object({
a: propertySchema,
b: propertySchema,
})
.refine(
(val) => {
return val.a === val.b;
},
{ message: "Must be equal" }
);
try {
schema.parse({
a: "asdf",
b: "qwer",
});
} catch (error) {
if (error instanceof z.ZodError) {
expect(error.flatten()).toEqual({
formErrors: ["Must be equal"],
fieldErrors: {},
});
}
}
try {
schema.parse({
a: null,
b: null,
});
} catch (_error) {
const error = _error as z.ZodError;
expect(error.flatten()).toEqual({
formErrors: [],
fieldErrors: {
a: ["Expected string, received null"],
b: ["Expected string, received null"],
},
});
expect(error.flatten((iss) => iss.message.toUpperCase())).toEqual({
formErrors: [],
fieldErrors: {
a: ["EXPECTED STRING, RECEIVED NULL"],
b: ["EXPECTED STRING, RECEIVED NULL"],
},
});
// Test identity
expect(error.flatten((i: z.ZodIssue) => i)).toEqual({
formErrors: [],
fieldErrors: {
a: [
{
code: "invalid_type",
expected: "string",
message: "Expected string, received null",
path: ["a"],
received: "null",
},
],
b: [
{
code: "invalid_type",
expected: "string",
message: "Expected string, received null",
path: ["b"],
received: "null",
},
],
},
});
// Test mapping
expect(error.flatten((i: z.ZodIssue) => i.message.length)).toEqual({
formErrors: [],
fieldErrors: {
a: ["Expected string, received null".length],
b: ["Expected string, received null".length],
},
});
}
});
@@ -0,0 +1,28 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
import { util } from "../helpers/util.js";
test("check any inference", () => {
const t1 = z.any();
t1.optional();
t1.nullable();
type t1 = z.infer<typeof t1>;
util.assertEqual<t1, any>(true);
});
test("check unknown inference", () => {
const t1 = z.unknown();
t1.optional();
t1.nullable();
type t1 = z.infer<typeof t1>;
util.assertEqual<t1, unknown>(true);
});
test("check never inference", () => {
const t1 = z.never();
expect(() => t1.parse(undefined)).toThrow();
expect(() => t1.parse("asdf")).toThrow();
expect(() => t1.parse(null)).toThrow();
});
@@ -0,0 +1,71 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
import { util } from "../helpers/util.js";
const minTwo = z.string().array().min(2);
const maxTwo = z.string().array().max(2);
const justTwo = z.string().array().length(2);
const intNum = z.string().array().nonempty();
const nonEmptyMax = z.string().array().nonempty().max(2);
type t1 = z.infer<typeof nonEmptyMax>;
util.assertEqual<[string, ...string[]], t1>(true);
type t2 = z.infer<typeof minTwo>;
util.assertEqual<string[], t2>(true);
test("passing validations", () => {
minTwo.parse(["a", "a"]);
minTwo.parse(["a", "a", "a"]);
maxTwo.parse(["a", "a"]);
maxTwo.parse(["a"]);
justTwo.parse(["a", "a"]);
intNum.parse(["a"]);
nonEmptyMax.parse(["a"]);
});
test("failing validations", () => {
expect(() => minTwo.parse(["a"])).toThrow();
expect(() => maxTwo.parse(["a", "a", "a"])).toThrow();
expect(() => justTwo.parse(["a"])).toThrow();
expect(() => justTwo.parse(["a", "a", "a"])).toThrow();
expect(() => intNum.parse([])).toThrow();
expect(() => nonEmptyMax.parse([])).toThrow();
expect(() => nonEmptyMax.parse(["a", "a", "a"])).toThrow();
});
test("parse empty array in nonempty", () => {
expect(() =>
z
.array(z.string())
.nonempty()
.parse([] as any)
).toThrow();
});
test("get element", () => {
justTwo.element.parse("asdf");
expect(() => justTwo.element.parse(12)).toThrow();
});
test("continue parsing despite array size error", () => {
const schema = z.object({
people: z.string().array().min(2),
});
const result = schema.safeParse({
people: [123],
});
expect(result.success).toEqual(false);
if (!result.success) {
expect(result.error.issues.length).toEqual(2);
}
});
test("parse should fail given sparse array", () => {
const schema = z.array(z.string()).nonempty().min(1).max(3);
expect(() => schema.parse(new Array(3))).toThrow();
});
@@ -0,0 +1,388 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
/// string
const stringSchema = z.string();
test("string async parse", async () => {
const goodData = "XXX";
const badData = 12;
const goodResult = await stringSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await stringSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// number
const numberSchema = z.number();
test("number async parse", async () => {
const goodData = 1234.2353;
const badData = "1234";
const goodResult = await numberSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await numberSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// bigInt
const bigIntSchema = z.bigint();
test("bigInt async parse", async () => {
const goodData = BigInt(145);
const badData = 134;
const goodResult = await bigIntSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await bigIntSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// boolean
const booleanSchema = z.boolean();
test("boolean async parse", async () => {
const goodData = true;
const badData = 1;
const goodResult = await booleanSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await booleanSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// date
const dateSchema = z.date();
test("date async parse", async () => {
const goodData = new Date();
const badData = new Date().toISOString();
const goodResult = await dateSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await dateSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// undefined
const undefinedSchema = z.undefined();
test("undefined async parse", async () => {
const goodData = undefined;
const badData = "XXX";
const goodResult = await undefinedSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(undefined);
const badResult = await undefinedSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// null
const nullSchema = z.null();
test("null async parse", async () => {
const goodData = null;
const badData = undefined;
const goodResult = await nullSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await nullSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// any
const anySchema = z.any();
test("any async parse", async () => {
const goodData = [{}];
// const badData = 'XXX';
const goodResult = await anySchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
// const badResult = await anySchema.safeParseAsync(badData);
// expect(badResult.success).toBe(false);
// if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// unknown
const unknownSchema = z.unknown();
test("unknown async parse", async () => {
const goodData = ["asdf", 124, () => {}];
// const badData = 'XXX';
const goodResult = await unknownSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
// const badResult = await unknownSchema.safeParseAsync(badData);
// expect(badResult.success).toBe(false);
// if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// void
const voidSchema = z.void();
test("void async parse", async () => {
const goodData = undefined;
const badData = 0;
const goodResult = await voidSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await voidSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// array
const arraySchema = z.array(z.string());
test("array async parse", async () => {
const goodData = ["XXX"];
const badData = "XXX";
const goodResult = await arraySchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await arraySchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// object
const objectSchema = z.object({ string: z.string() });
test("object async parse", async () => {
const goodData = { string: "XXX" };
const badData = { string: 12 };
const goodResult = await objectSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await objectSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// union
const unionSchema = z.union([z.string(), z.undefined()]);
test("union async parse", async () => {
const goodData = undefined;
const badData = null;
const goodResult = await unionSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await unionSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// record
const recordSchema = z.record(z.object({}));
test("record async parse", async () => {
const goodData = { adsf: {}, asdf: {} };
const badData = [{}];
const goodResult = await recordSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await recordSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// function
const functionSchema = z.function();
test("function async parse", async () => {
const goodData = () => {};
const badData = "XXX";
const goodResult = await functionSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(typeof goodResult.data).toEqual("function");
const badResult = await functionSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// literal
const literalSchema = z.literal("asdf");
test("literal async parse", async () => {
const goodData = "asdf";
const badData = "asdff";
const goodResult = await literalSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await literalSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// enum
const enumSchema = z.enum(["fish", "whale"]);
test("enum async parse", async () => {
const goodData = "whale";
const badData = "leopard";
const goodResult = await enumSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await enumSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// nativeEnum
enum nativeEnumTest {
asdf = "qwer",
}
// @ts-ignore
const nativeEnumSchema = z.nativeEnum(nativeEnumTest);
test("nativeEnum async parse", async () => {
const goodData = nativeEnumTest.asdf;
const badData = "asdf";
const goodResult = await nativeEnumSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) expect(goodResult.data).toEqual(goodData);
const badResult = await nativeEnumSchema.safeParseAsync(badData);
expect(badResult.success).toBe(false);
if (!badResult.success) expect(badResult.error).toBeInstanceOf(z.ZodError);
});
/// promise
const promiseSchema = z.promise(z.number());
test("promise async parse good", async () => {
const goodData = Promise.resolve(123);
const goodResult = await promiseSchema.safeParseAsync(goodData);
expect(goodResult.success).toBe(true);
if (goodResult.success) {
expect(goodResult.data).toBeInstanceOf(Promise);
const data = await goodResult.data;
expect(data).toEqual(123);
// expect(goodResult.data).resolves.toEqual(124);
// return goodResult.data;
} else {
throw new Error("success should be true");
}
});
test("promise async parse bad", async () => {
const badData = Promise.resolve("XXX");
const badResult = await promiseSchema.safeParseAsync(badData);
expect(badResult.success).toBe(true);
if (badResult.success) {
await expect(badResult.data).rejects.toBeInstanceOf(z.ZodError);
} else {
throw new Error("success should be true");
}
});
test("async validation non-empty strings", async () => {
const base = z.object({
hello: z.string().refine((x) => x && x.length > 0),
foo: z.string().refine((x) => x && x.length > 0),
});
const testval = { hello: "", foo: "" };
const result1 = base.safeParse(testval);
const result2 = base.safeParseAsync(testval);
const r1 = result1;
await result2.then((r2) => {
if (r1.success === false && r2.success === false) expect(r1.error.issues.length).toBe(r2.error.issues.length); // <--- r1 has length 2, r2 has length 1
});
});
test("async validation multiple errors 1", async () => {
const base = z.object({
hello: z.string(),
foo: z.number(),
});
const testval = { hello: 3, foo: "hello" };
const result1 = base.safeParse(testval);
const result2 = base.safeParseAsync(testval);
const r1 = result1;
await result2.then((r2) => {
if (r1.success === false && r2.success === false) expect(r2.error.issues.length).toBe(r1.error.issues.length);
});
});
test("async validation multiple errors 2", async () => {
const base = (is_async?: boolean) =>
z.object({
hello: z.string(),
foo: z.object({
bar: z.number().refine(is_async ? async () => false : () => false),
}),
});
const testval = { hello: 3, foo: { bar: 4 } };
const result1 = base().safeParse(testval);
const result2 = base(true).safeParseAsync(testval);
const r1 = result1;
await result2.then((r2) => {
if (r1.success === false && r2.success === false) expect(r2.error.issues.length).toBe(r1.error.issues.length);
});
});
test("ensure early async failure prevents follow-up refinement checks", async () => {
let count = 0;
const base = z.object({
hello: z.string(),
foo: z
.number()
.refine(async () => {
count++;
return true;
})
.refine(async () => {
count++;
return true;
}, "Good"),
});
const testval = { hello: "bye", foo: 3 };
const result = await base.safeParseAsync(testval);
if (result.success === false) {
expect(result.error.issues.length).toBe(1);
expect(count).toBe(1);
}
// await result.then((r) => {
// if (r.success === false) expect(r.error.issues.length).toBe(1);
// expect(count).toBe(2);
// });
});
@@ -0,0 +1,46 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
test("parse async test", async () => {
const schema1 = z.string().refine(async (_val) => false);
expect(() => schema1.parse("asdf")).toThrow();
const schema2 = z.string().refine((_val) => Promise.resolve(true));
return await expect(() => schema2.parse("asdf")).toThrow();
});
test("parseAsync async test", async () => {
const schema1 = z.string().refine(async (_val) => true);
await schema1.parseAsync("asdf");
const schema2 = z.string().refine(async (_val) => false);
return await expect(schema2.parseAsync("asdf")).rejects.toBeDefined();
// expect(async () => await schema2.parseAsync('asdf')).toThrow();
});
test("parseAsync async test", async () => {
// expect.assertions(2);
const schema1 = z.string().refine((_val) => Promise.resolve(true));
const v1 = await schema1.parseAsync("asdf");
expect(v1).toEqual("asdf");
const schema2 = z.string().refine((_val) => Promise.resolve(false));
await expect(schema2.parseAsync("asdf")).rejects.toBeDefined();
const schema3 = z.string().refine((_val) => Promise.resolve(true));
await expect(schema3.parseAsync("asdf")).resolves.toEqual("asdf");
return await expect(schema3.parseAsync("qwer")).resolves.toEqual("qwer");
});
test("parseAsync async with value", async () => {
const schema1 = z.string().refine(async (val) => {
return val.length > 5;
});
await expect(schema1.parseAsync("asdf")).rejects.toBeDefined();
const v = await schema1.parseAsync("asdf123");
return await expect(v).toEqual("asdf123");
});
@@ -0,0 +1,29 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
import { util } from "../helpers/util.js";
test("type guard", () => {
const stringToNumber = z.string().transform((arg) => arg.length);
const s1 = z.object({
stringToNumber,
});
type t1 = z.input<typeof s1>;
const data = { stringToNumber: "asdf" };
const parsed = s1.safeParse(data);
if (parsed.success) {
util.assertEqual<typeof data, t1>(true);
}
});
test("test this binding", () => {
const callback = (predicate: (val: string) => boolean) => {
return predicate("hello");
};
expect(callback((value) => z.string().safeParse(value).success)).toBe(true); // true
expect(callback((value) => z.string().safeParse(value).success)).toBe(true); // true
});
@@ -0,0 +1,55 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
const gtFive = z.bigint().gt(BigInt(5));
const gteFive = z.bigint().gte(BigInt(5));
const ltFive = z.bigint().lt(BigInt(5));
const lteFive = z.bigint().lte(BigInt(5));
const positive = z.bigint().positive();
const negative = z.bigint().negative();
const nonnegative = z.bigint().nonnegative();
const nonpositive = z.bigint().nonpositive();
const multipleOfFive = z.bigint().multipleOf(BigInt(5));
test("passing validations", () => {
z.bigint().parse(BigInt(1));
z.bigint().parse(BigInt(0));
z.bigint().parse(BigInt(-1));
gtFive.parse(BigInt(6));
gteFive.parse(BigInt(5));
gteFive.parse(BigInt(6));
ltFive.parse(BigInt(4));
lteFive.parse(BigInt(5));
lteFive.parse(BigInt(4));
positive.parse(BigInt(3));
negative.parse(BigInt(-2));
nonnegative.parse(BigInt(0));
nonnegative.parse(BigInt(7));
nonpositive.parse(BigInt(0));
nonpositive.parse(BigInt(-12));
multipleOfFive.parse(BigInt(15));
});
test("failing validations", () => {
expect(() => gtFive.parse(BigInt(5))).toThrow();
expect(() => gteFive.parse(BigInt(4))).toThrow();
expect(() => ltFive.parse(BigInt(5))).toThrow();
expect(() => lteFive.parse(BigInt(6))).toThrow();
expect(() => positive.parse(BigInt(0))).toThrow();
expect(() => positive.parse(BigInt(-2))).toThrow();
expect(() => negative.parse(BigInt(0))).toThrow();
expect(() => negative.parse(BigInt(3))).toThrow();
expect(() => nonnegative.parse(BigInt(-1))).toThrow();
expect(() => nonpositive.parse(BigInt(1))).toThrow();
expect(() => multipleOfFive.parse(BigInt(13))).toThrow();
});
test("min max getters", () => {
expect(z.bigint().min(BigInt(5)).minValue).toEqual(BigInt(5));
expect(z.bigint().min(BigInt(5)).min(BigInt(10)).minValue).toEqual(BigInt(10));
expect(z.bigint().max(BigInt(5)).maxValue).toEqual(BigInt(5));
expect(z.bigint().max(BigInt(5)).max(BigInt(1)).maxValue).toEqual(BigInt(1));
});
@@ -0,0 +1,53 @@
// @ts-ignore TS6133
import { test } from "vitest";
import * as z from "zod/v3";
import { util } from "../helpers/util.js";
test("branded types", () => {
const mySchema = z
.object({
name: z.string(),
})
.brand<"superschema">();
// simple branding
type MySchema = z.infer<typeof mySchema>;
util.assertEqual<MySchema, { name: string } & { [z.BRAND]: { superschema: true } }>(true);
const doStuff = (arg: MySchema) => arg;
doStuff(mySchema.parse({ name: "hello there" }));
// inheritance
const extendedSchema = mySchema.brand<"subschema">();
type ExtendedSchema = z.infer<typeof extendedSchema>;
util.assertEqual<ExtendedSchema, { name: string } & z.BRAND<"superschema"> & z.BRAND<"subschema">>(true);
doStuff(extendedSchema.parse({ name: "hello again" }));
// number branding
const numberSchema = z.number().brand<42>();
type NumberSchema = z.infer<typeof numberSchema>;
util.assertEqual<NumberSchema, number & { [z.BRAND]: { 42: true } }>(true);
// symbol branding
const MyBrand: unique symbol = Symbol("hello");
type MyBrand = typeof MyBrand;
const symbolBrand = z.number().brand<"sup">().brand<typeof MyBrand>();
type SymbolBrand = z.infer<typeof symbolBrand>;
// number & { [z.BRAND]: { sup: true, [MyBrand]: true } }
util.assertEqual<SymbolBrand, number & z.BRAND<"sup"> & z.BRAND<MyBrand>>(true);
// keeping brands out of input types
const age = z.number().brand<"age">();
type Age = z.infer<typeof age>;
type AgeInput = z.input<typeof age>;
util.assertEqual<AgeInput, Age>(false);
util.assertEqual<number, AgeInput>(true);
util.assertEqual<number & z.BRAND<"age">, Age>(true);
// @ts-expect-error
doStuff({ name: "hello there!" });
});
@@ -0,0 +1,220 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import { z } from "zod/v3";
import { util } from "../helpers/util.js";
test("basic catch", () => {
expect(z.string().catch("default").parse(undefined)).toBe("default");
});
test("catch fn does not run when parsing succeeds", () => {
let isCalled = false;
const cb = () => {
isCalled = true;
return "asdf";
};
expect(z.string().catch(cb).parse("test")).toBe("test");
expect(isCalled).toEqual(false);
});
test("basic catch async", async () => {
const result = await z.string().catch("default").parseAsync(1243);
expect(result).toBe("default");
});
test("catch replace wrong types", () => {
expect(z.string().catch("default").parse(true)).toBe("default");
expect(z.string().catch("default").parse(true)).toBe("default");
expect(z.string().catch("default").parse(15)).toBe("default");
expect(z.string().catch("default").parse([])).toBe("default");
expect(z.string().catch("default").parse(new Map())).toBe("default");
expect(z.string().catch("default").parse(new Set())).toBe("default");
expect(z.string().catch("default").parse({})).toBe("default");
});
test("catch with transform", () => {
const stringWithDefault = z
.string()
.transform((val) => val.toUpperCase())
.catch("default");
expect(stringWithDefault.parse(undefined)).toBe("default");
expect(stringWithDefault.parse(15)).toBe("default");
expect(stringWithDefault).toBeInstanceOf(z.ZodCatch);
expect(stringWithDefault._def.innerType).toBeInstanceOf(z.ZodEffects);
expect(stringWithDefault._def.innerType._def.schema).toBeInstanceOf(z.ZodSchema);
type inp = z.input<typeof stringWithDefault>;
util.assertEqual<inp, unknown>(true);
type out = z.output<typeof stringWithDefault>;
util.assertEqual<out, string>(true);
});
test("catch on existing optional", () => {
const stringWithDefault = z.string().optional().catch("asdf");
expect(stringWithDefault.parse(undefined)).toBe(undefined);
expect(stringWithDefault.parse(15)).toBe("asdf");
expect(stringWithDefault).toBeInstanceOf(z.ZodCatch);
expect(stringWithDefault._def.innerType).toBeInstanceOf(z.ZodOptional);
expect(stringWithDefault._def.innerType._def.innerType).toBeInstanceOf(z.ZodString);
type inp = z.input<typeof stringWithDefault>;
util.assertEqual<inp, unknown>(true);
type out = z.output<typeof stringWithDefault>;
util.assertEqual<out, string | undefined>(true);
});
test("optional on catch", () => {
const stringWithDefault = z.string().catch("asdf").optional();
type inp = z.input<typeof stringWithDefault>;
util.assertEqual<inp, unknown>(true);
type out = z.output<typeof stringWithDefault>;
util.assertEqual<out, string | undefined>(true);
});
test("complex chain example", () => {
const complex = z
.string()
.catch("asdf")
.transform((val) => val + "!")
.transform((val) => val.toUpperCase())
.catch("qwer")
.removeCatch()
.optional()
.catch("asdfasdf");
expect(complex.parse("qwer")).toBe("QWER!");
expect(complex.parse(15)).toBe("ASDF!");
expect(complex.parse(true)).toBe("ASDF!");
});
test("removeCatch", () => {
const stringWithRemovedDefault = z.string().catch("asdf").removeCatch();
type out = z.output<typeof stringWithRemovedDefault>;
util.assertEqual<out, string>(true);
});
test("nested", () => {
const inner = z.string().catch("asdf");
const outer = z.object({ inner }).catch({
inner: "asdf",
});
type input = z.input<typeof outer>;
util.assertEqual<input, unknown>(true);
type out = z.output<typeof outer>;
util.assertEqual<out, { inner: string }>(true);
expect(outer.parse(undefined)).toEqual({ inner: "asdf" });
expect(outer.parse({})).toEqual({ inner: "asdf" });
expect(outer.parse({ inner: undefined })).toEqual({ inner: "asdf" });
});
test("chained catch", () => {
const stringWithDefault = z.string().catch("inner").catch("outer");
const result = stringWithDefault.parse(undefined);
expect(result).toEqual("inner");
const resultDiff = stringWithDefault.parse(5);
expect(resultDiff).toEqual("inner");
});
test("factory", () => {
z.ZodCatch.create(z.string(), {
catch: "asdf",
}).parse(undefined);
});
test("native enum", () => {
enum Fruits {
apple = "apple",
orange = "orange",
}
const schema = z.object({
fruit: z.nativeEnum(Fruits).catch(Fruits.apple),
});
expect(schema.parse({})).toEqual({ fruit: Fruits.apple });
expect(schema.parse({ fruit: 15 })).toEqual({ fruit: Fruits.apple });
});
test("enum", () => {
const schema = z.object({
fruit: z.enum(["apple", "orange"]).catch("apple"),
});
expect(schema.parse({})).toEqual({ fruit: "apple" });
expect(schema.parse({ fruit: true })).toEqual({ fruit: "apple" });
expect(schema.parse({ fruit: 15 })).toEqual({ fruit: "apple" });
});
test("reported issues with nested usage", () => {
const schema = z.object({
string: z.string(),
obj: z.object({
sub: z.object({
lit: z.literal("a"),
subCatch: z.number().catch(23),
}),
midCatch: z.number().catch(42),
}),
number: z.number().catch(0),
bool: z.boolean(),
});
try {
schema.parse({
string: {},
obj: {
sub: {
lit: "b",
subCatch: "24",
},
midCatch: 444,
},
number: "",
bool: "yes",
});
} catch (error) {
const issues = (error as z.ZodError).issues;
expect(issues.length).toEqual(3);
expect(issues[0].message).toMatch("string");
expect(issues[1].message).toMatch("literal");
expect(issues[2].message).toMatch("boolean");
}
});
test("catch error", () => {
let catchError: z.ZodError | undefined = undefined;
const schema = z.object({
age: z.number(),
name: z.string().catch((ctx) => {
catchError = ctx.error;
return "John Doe";
}),
});
const result = schema.safeParse({
age: null,
name: null,
});
expect(result.success).toEqual(false);
expect(!result.success && result.error.issues.length).toEqual(1);
expect(!result.success && result.error.issues[0].message).toMatch("number");
expect(catchError).toBeInstanceOf(z.ZodError);
expect(catchError !== undefined && (catchError as z.ZodError).issues.length).toEqual(1);
expect(catchError !== undefined && (catchError as z.ZodError).issues[0].message).toMatch("string");
});
test("ctx.input", () => {
const schema = z.string().catch((ctx) => {
return String(ctx.input);
});
expect(schema.parse(123)).toEqual("123");
});
@@ -0,0 +1,133 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
test("string coercion", () => {
const schema = z.coerce.string();
expect(schema.parse("sup")).toEqual("sup");
expect(schema.parse("")).toEqual("");
expect(schema.parse(12)).toEqual("12");
expect(schema.parse(0)).toEqual("0");
expect(schema.parse(-12)).toEqual("-12");
expect(schema.parse(3.14)).toEqual("3.14");
expect(schema.parse(BigInt(15))).toEqual("15");
expect(schema.parse(Number.NaN)).toEqual("NaN");
expect(schema.parse(Number.POSITIVE_INFINITY)).toEqual("Infinity");
expect(schema.parse(Number.NEGATIVE_INFINITY)).toEqual("-Infinity");
expect(schema.parse(true)).toEqual("true");
expect(schema.parse(false)).toEqual("false");
expect(schema.parse(null)).toEqual("null");
expect(schema.parse(undefined)).toEqual("undefined");
expect(schema.parse({ hello: "world!" })).toEqual("[object Object]");
expect(schema.parse(["item", "another_item"])).toEqual("item,another_item");
expect(schema.parse([])).toEqual("");
expect(schema.parse(new Date("2022-01-01T00:00:00.000Z"))).toEqual(new Date("2022-01-01T00:00:00.000Z").toString());
});
test("number coercion", () => {
const schema = z.coerce.number();
expect(schema.parse("12")).toEqual(12);
expect(schema.parse("0")).toEqual(0);
expect(schema.parse("-12")).toEqual(-12);
expect(schema.parse("3.14")).toEqual(3.14);
expect(schema.parse("")).toEqual(0);
expect(() => schema.parse("NOT_A_NUMBER")).toThrow(); // z.ZodError
expect(schema.parse(12)).toEqual(12);
expect(schema.parse(0)).toEqual(0);
expect(schema.parse(-12)).toEqual(-12);
expect(schema.parse(3.14)).toEqual(3.14);
expect(schema.parse(BigInt(15))).toEqual(15);
expect(() => schema.parse(Number.NaN)).toThrow(); // z.ZodError
expect(schema.parse(Number.POSITIVE_INFINITY)).toEqual(Number.POSITIVE_INFINITY);
expect(schema.parse(Number.NEGATIVE_INFINITY)).toEqual(Number.NEGATIVE_INFINITY);
expect(schema.parse(true)).toEqual(1);
expect(schema.parse(false)).toEqual(0);
expect(schema.parse(null)).toEqual(0);
expect(() => schema.parse(undefined)).toThrow(); // z.ZodError
expect(() => schema.parse({ hello: "world!" })).toThrow(); // z.ZodError
expect(() => schema.parse(["item", "another_item"])).toThrow(); // z.ZodError
expect(schema.parse([])).toEqual(0);
expect(schema.parse(new Date(1670139203496))).toEqual(1670139203496);
});
test("boolean coercion", () => {
const schema = z.coerce.boolean();
expect(schema.parse("true")).toEqual(true);
expect(schema.parse("false")).toEqual(true);
expect(schema.parse("0")).toEqual(true);
expect(schema.parse("1")).toEqual(true);
expect(schema.parse("")).toEqual(false);
expect(schema.parse(1)).toEqual(true);
expect(schema.parse(0)).toEqual(false);
expect(schema.parse(-1)).toEqual(true);
expect(schema.parse(3.14)).toEqual(true);
expect(schema.parse(BigInt(15))).toEqual(true);
expect(schema.parse(Number.NaN)).toEqual(false);
expect(schema.parse(Number.POSITIVE_INFINITY)).toEqual(true);
expect(schema.parse(Number.NEGATIVE_INFINITY)).toEqual(true);
expect(schema.parse(true)).toEqual(true);
expect(schema.parse(false)).toEqual(false);
expect(schema.parse(null)).toEqual(false);
expect(schema.parse(undefined)).toEqual(false);
expect(schema.parse({ hello: "world!" })).toEqual(true);
expect(schema.parse(["item", "another_item"])).toEqual(true);
expect(schema.parse([])).toEqual(true);
expect(schema.parse(new Date(1670139203496))).toEqual(true);
});
test("bigint coercion", () => {
const schema = z.coerce.bigint();
expect(schema.parse("5")).toEqual(BigInt(5));
expect(schema.parse("0")).toEqual(BigInt(0));
expect(schema.parse("-5")).toEqual(BigInt(-5));
expect(() => schema.parse("3.14")).toThrow(); // not a z.ZodError!
expect(schema.parse("")).toEqual(BigInt(0));
expect(() => schema.parse("NOT_A_NUMBER")).toThrow(); // not a z.ZodError!
expect(schema.parse(5)).toEqual(BigInt(5));
expect(schema.parse(0)).toEqual(BigInt(0));
expect(schema.parse(-5)).toEqual(BigInt(-5));
expect(() => schema.parse(3.14)).toThrow(); // not a z.ZodError!
expect(schema.parse(BigInt(5))).toEqual(BigInt(5));
expect(() => schema.parse(Number.NaN)).toThrow(); // not a z.ZodError!
expect(() => schema.parse(Number.POSITIVE_INFINITY)).toThrow(); // not a z.ZodError!
expect(() => schema.parse(Number.NEGATIVE_INFINITY)).toThrow(); // not a z.ZodError!
expect(schema.parse(true)).toEqual(BigInt(1));
expect(schema.parse(false)).toEqual(BigInt(0));
expect(() => schema.parse(null)).toThrow(); // not a z.ZodError!
expect(() => schema.parse(undefined)).toThrow(); // not a z.ZodError!
expect(() => schema.parse({ hello: "world!" })).toThrow(); // not a z.ZodError!
expect(() => schema.parse(["item", "another_item"])).toThrow(); // not a z.ZodError!
expect(schema.parse([])).toEqual(BigInt(0));
expect(schema.parse(new Date(1670139203496))).toEqual(BigInt(1670139203496));
});
test("date coercion", () => {
const schema = z.coerce.date();
expect(schema.parse(new Date().toDateString())).toBeInstanceOf(Date);
expect(schema.parse(new Date().toISOString())).toBeInstanceOf(Date);
expect(schema.parse(new Date().toUTCString())).toBeInstanceOf(Date);
expect(schema.parse("5")).toBeInstanceOf(Date);
expect(schema.parse("2000-01-01")).toBeInstanceOf(Date);
// expect(schema.parse("0")).toBeInstanceOf(Date);
// expect(schema.parse("-5")).toBeInstanceOf(Date);
// expect(schema.parse("3.14")).toBeInstanceOf(Date);
expect(() => schema.parse("")).toThrow(); // z.ZodError
expect(() => schema.parse("NOT_A_DATE")).toThrow(); // z.ZodError
expect(schema.parse(5)).toBeInstanceOf(Date);
expect(schema.parse(0)).toBeInstanceOf(Date);
expect(schema.parse(-5)).toBeInstanceOf(Date);
expect(schema.parse(3.14)).toBeInstanceOf(Date);
expect(() => schema.parse(BigInt(5))).toThrow(); // not a z.ZodError!
expect(() => schema.parse(Number.NaN)).toThrow(); // z.ZodError
expect(() => schema.parse(Number.POSITIVE_INFINITY)).toThrow(); // z.ZodError
expect(() => schema.parse(Number.NEGATIVE_INFINITY)).toThrow(); // z.ZodError
expect(schema.parse(true)).toBeInstanceOf(Date);
expect(schema.parse(false)).toBeInstanceOf(Date);
expect(schema.parse(null)).toBeInstanceOf(Date);
expect(() => schema.parse(undefined)).toThrow(); // z.ZodError
expect(() => schema.parse({ hello: "world!" })).toThrow(); // z.ZodError
expect(() => schema.parse(["item", "another_item"])).toThrow(); // z.ZodError
expect(() => schema.parse([])).toThrow(); // z.ZodError
expect(schema.parse(new Date())).toBeInstanceOf(Date);
});
@@ -0,0 +1,70 @@
import { expect, test } from "vitest";
import * as z from "zod/v3";
const crazySchema = z.object({
tuple: z.tuple([
z.string().nullable().optional(),
z.number().nullable().optional(),
z.boolean().nullable().optional(),
z.null().nullable().optional(),
z.undefined().nullable().optional(),
z.literal("1234").nullable().optional(),
]),
merged: z
.object({
k1: z.string().optional(),
})
.merge(z.object({ k1: z.string().nullable(), k2: z.number() })),
union: z.array(z.union([z.literal("asdf"), z.literal(12)])).nonempty(),
array: z.array(z.number()),
// sumTransformer: z.transformer(z.array(z.number()), z.number(), (arg) => {
// return arg.reduce((a, b) => a + b, 0);
// }),
sumMinLength: z.array(z.number()).refine((arg) => arg.length > 5),
intersection: z.intersection(z.object({ p1: z.string().optional() }), z.object({ p1: z.number().optional() })),
enum: z.intersection(z.enum(["zero", "one"]), z.enum(["one", "two"])),
nonstrict: z.object({ points: z.number() }).nonstrict(),
numProm: z.promise(z.number()),
lenfun: z.function(z.tuple([z.string()]), z.boolean()),
});
// const asyncCrazySchema = crazySchema.extend({
// // async_transform: z.transformer(
// // z.array(z.number()),
// // z.number(),
// // async (arg) => {
// // return arg.reduce((a, b) => a + b, 0);
// // }
// // ),
// async_refine: z.array(z.number()).refine(async (arg) => arg.length > 5),
// });
test("parse", () => {
const input = {
tuple: ["asdf", 1234, true, null, undefined, "1234"],
merged: { k1: "asdf", k2: 12 },
union: ["asdf", 12, "asdf", 12, "asdf", 12],
array: [12, 15, 16],
// sumTransformer: [12, 15, 16],
sumMinLength: [12, 15, 16, 98, 24, 63],
intersection: {},
enum: "one",
nonstrict: { points: 1234 },
numProm: Promise.resolve(12),
lenfun: (x: string) => x.length,
};
const result = crazySchema.parse(input);
// Verify the parsed result structure
expect(result.tuple).toEqual(input.tuple);
expect(result.merged).toEqual(input.merged);
expect(result.union).toEqual(input.union);
expect(result.array).toEqual(input.array);
expect(result.sumMinLength).toEqual(input.sumMinLength);
expect(result.intersection).toEqual(input.intersection);
expect(result.enum).toEqual(input.enum);
expect(result.nonstrict).toEqual(input.nonstrict);
expect(result.numProm).toBeInstanceOf(Promise);
expect(typeof result.lenfun).toBe("function");
});
@@ -0,0 +1,31 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
test("passing validations", () => {
const example1 = z.custom<number>((x) => typeof x === "number");
example1.parse(1234);
expect(() => example1.parse({})).toThrow();
});
test("string params", () => {
const example1 = z.custom<number>((x) => typeof x !== "number", "customerr");
const result = example1.safeParse(1234);
expect(result.success).toEqual(false);
// @ts-ignore
expect(JSON.stringify(result.error).includes("customerr")).toEqual(true);
});
test("async validations", async () => {
const example1 = z.custom<number>(async (x) => {
return typeof x === "number";
});
const r1 = await example1.safeParseAsync(1234);
expect(r1.success).toEqual(true);
expect(r1.data).toEqual(1234);
const r2 = await example1.safeParseAsync("asdf");
expect(r2.success).toEqual(false);
expect(r2.error!.issues.length).toEqual(1);
});
@@ -0,0 +1,32 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
const beforeBenchmarkDate = new Date(2022, 10, 4);
const benchmarkDate = new Date(2022, 10, 5);
const afterBenchmarkDate = new Date(2022, 10, 6);
const minCheck = z.date().min(benchmarkDate);
const maxCheck = z.date().max(benchmarkDate);
test("passing validations", () => {
minCheck.parse(benchmarkDate);
minCheck.parse(afterBenchmarkDate);
maxCheck.parse(benchmarkDate);
maxCheck.parse(beforeBenchmarkDate);
});
test("failing validations", () => {
expect(() => minCheck.parse(beforeBenchmarkDate)).toThrow();
expect(() => maxCheck.parse(afterBenchmarkDate)).toThrow();
});
test("min max getters", () => {
expect(minCheck.minDate).toEqual(benchmarkDate);
expect(minCheck.min(afterBenchmarkDate).minDate).toEqual(afterBenchmarkDate);
expect(maxCheck.maxDate).toEqual(benchmarkDate);
expect(maxCheck.max(beforeBenchmarkDate).maxDate).toEqual(beforeBenchmarkDate);
});
@@ -0,0 +1,186 @@
// @ts-ignore TS6133
import { test } from "vitest";
import * as z from "zod/v3";
test("test", () => {
z;
});
// const fish = z.object({
// name: z.string(),
// props: z.object({
// color: z.string(),
// numScales: z.number(),
// }),
// });
// const nonStrict = z
// .object({
// name: z.string(),
// color: z.string(),
// })
// .nonstrict();
// test('object pick type', () => {
// const modNonStrictFish = nonStrict.omit({ name: true });
// modNonStrictFish.parse({ color: 'asdf' });
// const bad1 = () => fish.pick({ props: { unknown: true } } as any);
// const bad2 = () => fish.omit({ name: true, props: { unknown: true } } as any);
// expect(bad1).toThrow();
// expect(bad2).toThrow();
// });
// test('f1', () => {
// const f1 = fish.pick(true);
// f1.parse({ name: 'a', props: { color: 'b', numScales: 3 } });
// });
// test('f2', () => {
// const f2 = fish.pick({ props: true });
// f2.parse({ props: { color: 'asdf', numScales: 1 } });
// const badcheck2 = () => f2.parse({ name: 'a', props: { color: 'b', numScales: 3 } } as any);
// expect(badcheck2).toThrow();
// });
// test('f3', () => {
// const f3 = fish.pick({ props: { color: true } });
// f3.parse({ props: { color: 'b' } });
// const badcheck3 = () => f3.parse({ name: 'a', props: { color: 'b', numScales: 3 } } as any);
// expect(badcheck3).toThrow();
// });
// test('f4', () => {
// const badcheck4 = () => fish.pick({ props: { color: true, unknown: true } });
// expect(badcheck4).toThrow();
// });
// test('f6', () => {
// const f6 = fish.omit({ props: true });
// const badcheck6 = () => f6.parse({ name: 'a', props: { color: 'b', numScales: 3 } } as any);
// f6.parse({ name: 'adsf' });
// expect(badcheck6).toThrow();
// });
// test('f7', () => {
// const f7 = fish.omit({ props: { color: true } });
// f7.parse({ name: 'a', props: { numScales: 3 } });
// const badcheck7 = () => f7.parse({ name: 'a', props: { color: 'b', numScales: 3 } } as any);
// expect(badcheck7).toThrow();
// });
// test('f8', () => {
// const badcheck8 = () => fish.omit({ props: { color: true, unknown: true } });
// expect(badcheck8).toThrow();
// });
// test('f9', () => {
// const f9 = nonStrict.pick(true);
// f9.parse({ name: 'a', color: 'asdf' });
// });
// test('f10', () => {
// const f10 = nonStrict.pick({ name: true });
// f10.parse({ name: 'a' });
// const val = f10.parse({ name: 'a', color: 'b' });
// expect(val).toEqual({ name: 'a' });
// });
// test('f12', () => {
// const badfcheck12 = () => nonStrict.omit({ color: true, asdf: true });
// expect(badfcheck12).toThrow();
// });
// test('array masking', () => {
// const fishArray = z.array(fish);
// const modFishArray = fishArray.pick({
// name: true,
// props: {
// numScales: true,
// },
// });
// modFishArray.parse([{ name: 'fish', props: { numScales: 12 } }]);
// const bad1 = () => modFishArray.parse([{ name: 'fish', props: { numScales: 12, color: 'asdf' } }] as any);
// expect(bad1).toThrow();
// });
// test('array masking', () => {
// const fishArray = z.array(fish);
// const fail = () =>
// fishArray.pick({
// name: true,
// props: {
// whatever: true,
// },
// } as any);
// expect(fail).toThrow();
// });
// test('array masking', () => {
// const fishArray = z.array(fish);
// const fail = () =>
// fishArray.omit({
// whateve: true,
// } as any);
// expect(fail).toThrow();
// });
// test('array masking', () => {
// const fishArray = z.array(fish);
// const modFishList = fishArray.omit({
// name: true,
// props: {
// color: true,
// },
// });
// modFishList.parse([{ props: { numScales: 12 } }]);
// const fail = () => modFishList.parse([{ name: 'hello', props: { numScales: 12 } }] as any);
// expect(fail).toThrow();
// });
// test('primitive array masking', () => {
// const fishArray = z.array(z.number());
// const fail = () => fishArray.pick({} as any);
// expect(fail).toThrow();
// });
// test('other array masking', () => {
// const fishArray = z.array(z.array(z.number()));
// const fail = () => fishArray.pick({} as any);
// expect(fail).toThrow();
// });
// test('invalid mask #1', () => {
// const fail = () => fish.pick(1 as any);
// expect(fail).toThrow();
// });
// test('invalid mask #2', () => {
// const fail = () => fish.pick([] as any);
// expect(fail).toThrow();
// });
// test('invalid mask #3', () => {
// const fail = () => fish.pick(false as any);
// expect(fail).toThrow();
// });
// test('invalid mask #4', () => {
// const fail = () => fish.pick('asdf' as any);
// expect(fail).toThrow();
// });
// test('invalid mask #5', () => {
// const fail = () => fish.omit(1 as any);
// expect(fail).toThrow();
// });
// test('invalid mask #6', () => {
// const fail = () => fish.omit([] as any);
// expect(fail).toThrow();
// });
// test('invalid mask #7', () => {
// const fail = () => fish.omit(false as any);
// expect(fail).toThrow();
// });
// test('invalid mask #8', () => {
// const fail = () => fish.omit('asdf' as any);
// expect(fail).toThrow();
// });
@@ -0,0 +1,112 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import { z } from "zod/v3";
import { util } from "../helpers/util.js";
test("basic defaults", () => {
expect(z.string().default("default").parse(undefined)).toBe("default");
});
test("default with transform", () => {
const stringWithDefault = z
.string()
.transform((val) => val.toUpperCase())
.default("default");
expect(stringWithDefault.parse(undefined)).toBe("DEFAULT");
expect(stringWithDefault).toBeInstanceOf(z.ZodDefault);
expect(stringWithDefault._def.innerType).toBeInstanceOf(z.ZodEffects);
expect(stringWithDefault._def.innerType._def.schema).toBeInstanceOf(z.ZodSchema);
type inp = z.input<typeof stringWithDefault>;
util.assertEqual<inp, string | undefined>(true);
type out = z.output<typeof stringWithDefault>;
util.assertEqual<out, string>(true);
});
test("default on existing optional", () => {
const stringWithDefault = z.string().optional().default("asdf");
expect(stringWithDefault.parse(undefined)).toBe("asdf");
expect(stringWithDefault).toBeInstanceOf(z.ZodDefault);
expect(stringWithDefault._def.innerType).toBeInstanceOf(z.ZodOptional);
expect(stringWithDefault._def.innerType._def.innerType).toBeInstanceOf(z.ZodString);
type inp = z.input<typeof stringWithDefault>;
util.assertEqual<inp, string | undefined>(true);
type out = z.output<typeof stringWithDefault>;
util.assertEqual<out, string>(true);
});
test("optional on default", () => {
const stringWithDefault = z.string().default("asdf").optional();
type inp = z.input<typeof stringWithDefault>;
util.assertEqual<inp, string | undefined>(true);
type out = z.output<typeof stringWithDefault>;
util.assertEqual<out, string | undefined>(true);
});
test("complex chain example", () => {
const complex = z
.string()
.default("asdf")
.transform((val) => val.toUpperCase())
.default("qwer")
.removeDefault()
.optional()
.default("asdfasdf");
expect(complex.parse(undefined)).toBe("ASDFASDF");
});
test("removeDefault", () => {
const stringWithRemovedDefault = z.string().default("asdf").removeDefault();
type out = z.output<typeof stringWithRemovedDefault>;
util.assertEqual<out, string>(true);
});
test("nested", () => {
const inner = z.string().default("asdf");
const outer = z.object({ inner }).default({
inner: undefined,
});
type input = z.input<typeof outer>;
util.assertEqual<input, { inner?: string | undefined } | undefined>(true);
type out = z.output<typeof outer>;
util.assertEqual<out, { inner: string }>(true);
expect(outer.parse(undefined)).toEqual({ inner: "asdf" });
expect(outer.parse({})).toEqual({ inner: "asdf" });
expect(outer.parse({ inner: undefined })).toEqual({ inner: "asdf" });
});
test("chained defaults", () => {
const stringWithDefault = z.string().default("inner").default("outer");
const result = stringWithDefault.parse(undefined);
expect(result).toEqual("outer");
});
test("factory", () => {
expect(z.ZodDefault.create(z.string(), { default: "asdf" }).parse(undefined)).toEqual("asdf");
});
test("native enum", () => {
enum Fruits {
apple = "apple",
orange = "orange",
}
const schema = z.object({
fruit: z.nativeEnum(Fruits).default(Fruits.apple),
});
expect(schema.parse({})).toEqual({ fruit: Fruits.apple });
});
test("enum", () => {
const schema = z.object({
fruit: z.enum(["apple", "orange"]).default("apple"),
});
expect(schema.parse({})).toEqual({ fruit: "apple" });
});
@@ -0,0 +1,33 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
const description = "a description";
test("passing `description` to schema should add a description", () => {
expect(z.string({ description }).description).toEqual(description);
expect(z.number({ description }).description).toEqual(description);
expect(z.boolean({ description }).description).toEqual(description);
});
test("`.describe` should add a description", () => {
expect(z.string().describe(description).description).toEqual(description);
expect(z.number().describe(description).description).toEqual(description);
expect(z.boolean().describe(description).description).toEqual(description);
});
test("description should carry over to chained schemas", () => {
const schema = z.string({ description });
expect(schema.description).toEqual(description);
expect(schema.optional().description).toEqual(description);
expect(schema.optional().nullable().default("default").description).toEqual(description);
});
test("description should not carry over to chained array schema", () => {
const schema = z.string().describe(description);
expect(schema.description).toEqual(description);
expect(schema.array().description).toEqual(undefined);
expect(z.array(schema).description).toEqual(undefined);
});
@@ -0,0 +1,315 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
test("valid", () => {
expect(
z
.discriminatedUnion("type", [
z.object({ type: z.literal("a"), a: z.string() }),
z.object({ type: z.literal("b"), b: z.string() }),
])
.parse({ type: "a", a: "abc" })
).toEqual({ type: "a", a: "abc" });
});
test("valid - discriminator value of various primitive types", () => {
const schema = z.discriminatedUnion("type", [
z.object({ type: z.literal("1"), val: z.literal(1) }),
z.object({ type: z.literal(1), val: z.literal(2) }),
z.object({ type: z.literal(BigInt(1)), val: z.literal(3) }),
z.object({ type: z.literal("true"), val: z.literal(4) }),
z.object({ type: z.literal(true), val: z.literal(5) }),
z.object({ type: z.literal("null"), val: z.literal(6) }),
z.object({ type: z.literal(null), val: z.literal(7) }),
z.object({ type: z.literal("undefined"), val: z.literal(8) }),
z.object({ type: z.literal(undefined), val: z.literal(9) }),
z.object({ type: z.literal("transform"), val: z.literal(10) }),
z.object({ type: z.literal("refine"), val: z.literal(11) }),
z.object({ type: z.literal("superRefine"), val: z.literal(12) }),
]);
expect(schema.parse({ type: "1", val: 1 })).toEqual({ type: "1", val: 1 });
expect(schema.parse({ type: 1, val: 2 })).toEqual({ type: 1, val: 2 });
expect(schema.parse({ type: BigInt(1), val: 3 })).toEqual({
type: BigInt(1),
val: 3,
});
expect(schema.parse({ type: "true", val: 4 })).toEqual({
type: "true",
val: 4,
});
expect(schema.parse({ type: true, val: 5 })).toEqual({
type: true,
val: 5,
});
expect(schema.parse({ type: "null", val: 6 })).toEqual({
type: "null",
val: 6,
});
expect(schema.parse({ type: null, val: 7 })).toEqual({
type: null,
val: 7,
});
expect(schema.parse({ type: "undefined", val: 8 })).toEqual({
type: "undefined",
val: 8,
});
expect(schema.parse({ type: undefined, val: 9 })).toEqual({
type: undefined,
val: 9,
});
});
test("invalid - null", () => {
try {
z.discriminatedUnion("type", [
z.object({ type: z.literal("a"), a: z.string() }),
z.object({ type: z.literal("b"), b: z.string() }),
]).parse(null);
throw new Error();
} catch (e: any) {
expect(JSON.parse(e.message)).toEqual([
{
code: z.ZodIssueCode.invalid_type,
expected: z.ZodParsedType.object,
message: "Expected object, received null",
received: z.ZodParsedType.null,
path: [],
},
]);
}
});
test("invalid discriminator value", () => {
try {
z.discriminatedUnion("type", [
z.object({ type: z.literal("a"), a: z.string() }),
z.object({ type: z.literal("b"), b: z.string() }),
]).parse({ type: "x", a: "abc" });
throw new Error();
} catch (e: any) {
expect(JSON.parse(e.message)).toEqual([
{
code: z.ZodIssueCode.invalid_union_discriminator,
options: ["a", "b"],
message: "Invalid discriminator value. Expected 'a' | 'b'",
path: ["type"],
},
]);
}
});
test("valid discriminator value, invalid data", () => {
try {
z.discriminatedUnion("type", [
z.object({ type: z.literal("a"), a: z.string() }),
z.object({ type: z.literal("b"), b: z.string() }),
]).parse({ type: "a", b: "abc" });
throw new Error();
} catch (e: any) {
expect(JSON.parse(e.message)).toEqual([
{
code: z.ZodIssueCode.invalid_type,
expected: z.ZodParsedType.string,
message: "Required",
path: ["a"],
received: z.ZodParsedType.undefined,
},
]);
}
});
test("wrong schema - missing discriminator", () => {
try {
z.discriminatedUnion("type", [
z.object({ type: z.literal("a"), a: z.string() }),
z.object({ b: z.string() }) as any,
]);
throw new Error();
} catch (e: any) {
expect(e.message.includes("could not be extracted")).toBe(true);
}
});
test("wrong schema - duplicate discriminator values", () => {
try {
z.discriminatedUnion("type", [
z.object({ type: z.literal("a"), a: z.string() }),
z.object({ type: z.literal("a"), b: z.string() }),
]);
throw new Error();
} catch (e: any) {
expect(e.message.includes("has duplicate value")).toEqual(true);
}
});
test("async - valid", async () => {
expect(
await z
.discriminatedUnion("type", [
z.object({
type: z.literal("a"),
a: z
.string()
.refine(async () => true)
.transform(async (val) => Number(val)),
}),
z.object({
type: z.literal("b"),
b: z.string(),
}),
])
.parseAsync({ type: "a", a: "1" })
).toEqual({ type: "a", a: 1 });
});
test("async - invalid", async () => {
try {
await z
.discriminatedUnion("type", [
z.object({
type: z.literal("a"),
a: z
.string()
.refine(async () => true)
.transform(async (val) => val),
}),
z.object({
type: z.literal("b"),
b: z.string(),
}),
])
.parseAsync({ type: "a", a: 1 });
throw new Error();
} catch (e: any) {
expect(JSON.parse(e.message)).toEqual([
{
code: "invalid_type",
expected: "string",
received: "number",
path: ["a"],
message: "Expected string, received number",
},
]);
}
});
test("valid - literals with .default or .preprocess", () => {
const schema = z.discriminatedUnion("type", [
z.object({
type: z.literal("foo").default("foo"),
a: z.string(),
}),
z.object({
type: z.literal("custom"),
method: z.string(),
}),
z.object({
type: z.preprocess((val) => String(val), z.literal("bar")),
c: z.string(),
}),
]);
expect(schema.parse({ type: "foo", a: "foo" })).toEqual({
type: "foo",
a: "foo",
});
});
test("enum and nativeEnum", () => {
enum MyEnum {
d = 0,
e = "e",
}
const schema = z.discriminatedUnion("key", [
z.object({
key: z.literal("a"),
// Add other properties specific to this option
}),
z.object({
key: z.enum(["b", "c"]),
// Add other properties specific to this option
}),
z.object({
key: z.nativeEnum(MyEnum),
// Add other properties specific to this option
}),
]);
// type schema = z.infer<typeof schema>;
schema.parse({ key: "a" });
schema.parse({ key: "b" });
schema.parse({ key: "c" });
schema.parse({ key: MyEnum.d });
schema.parse({ key: MyEnum.e });
schema.parse({ key: "e" });
});
test("branded", () => {
const schema = z.discriminatedUnion("key", [
z.object({
key: z.literal("a"),
// Add other properties specific to this option
}),
z.object({
key: z.literal("b").brand("asdfaf"),
// Add other properties specific to this option
}),
]);
// type schema = z.infer<typeof schema>;
schema.parse({ key: "a" });
schema.parse({ key: "b" });
expect(() => {
schema.parse({ key: "c" });
}).toThrow();
});
test("optional and nullable", () => {
const schema = z.discriminatedUnion("key", [
z.object({
key: z.literal("a").optional(),
a: z.literal(true),
}),
z.object({
key: z.literal("b").nullable(),
b: z.literal(true),
// Add other properties specific to this option
}),
]);
type schema = z.infer<typeof schema>;
z.util.assertEqual<schema, { key?: "a" | undefined; a: true } | { key: "b" | null; b: true }>(true);
schema.parse({ key: "a", a: true });
schema.parse({ key: undefined, a: true });
schema.parse({ key: "b", b: true });
schema.parse({ key: null, b: true });
expect(() => {
schema.parse({ key: null, a: true });
}).toThrow();
expect(() => {
schema.parse({ key: "b", a: true });
}).toThrow();
const value = schema.parse({ key: null, b: true });
if (!("key" in value)) value.a;
if (value.key === undefined) value.a;
if (value.key === "a") value.a;
if (value.key === "b") value.b;
if (value.key === null) value.b;
});
test("readonly array of options", () => {
const options = [
z.object({ type: z.literal("x"), val: z.literal(1) }),
z.object({ type: z.literal("y"), val: z.literal(2) }),
] as const;
expect(z.discriminatedUnion("type", options).parse({ type: "x", val: 1 })).toEqual({ type: "x", val: 1 });
});
@@ -0,0 +1,80 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
import { util } from "../helpers/util.js";
test("create enum", () => {
const MyEnum = z.enum(["Red", "Green", "Blue"]);
expect(MyEnum.Values.Red).toEqual("Red");
expect(MyEnum.Enum.Red).toEqual("Red");
expect(MyEnum.enum.Red).toEqual("Red");
});
test("infer enum", () => {
const MyEnum = z.enum(["Red", "Green", "Blue"]);
type MyEnum = z.infer<typeof MyEnum>;
util.assertEqual<MyEnum, "Red" | "Green" | "Blue">(true);
});
test("get options", () => {
expect(z.enum(["tuna", "trout"]).options).toEqual(["tuna", "trout"]);
});
test("readonly enum", () => {
const HTTP_SUCCESS = ["200", "201"] as const;
const arg = z.enum(HTTP_SUCCESS);
type arg = z.infer<typeof arg>;
util.assertEqual<arg, "200" | "201">(true);
arg.parse("201");
expect(() => arg.parse("202")).toThrow();
});
test("error params", () => {
const result = z.enum(["test"], { required_error: "REQUIRED" }).safeParse(undefined);
expect(result.success).toEqual(false);
if (!result.success) {
expect(result.error.issues[0].message).toEqual("REQUIRED");
}
});
test("extract/exclude", () => {
const foods = ["Pasta", "Pizza", "Tacos", "Burgers", "Salad"] as const;
const FoodEnum = z.enum(foods);
const ItalianEnum = FoodEnum.extract(["Pasta", "Pizza"]);
const UnhealthyEnum = FoodEnum.exclude(["Salad"]);
const EmptyFoodEnum = FoodEnum.exclude(foods);
util.assertEqual<z.infer<typeof ItalianEnum>, "Pasta" | "Pizza">(true);
util.assertEqual<z.infer<typeof UnhealthyEnum>, "Pasta" | "Pizza" | "Tacos" | "Burgers">(true);
// @ts-expect-error TS2344
util.assertEqual<typeof EmptyFoodEnum, z.ZodEnum<[]>>(true);
util.assertEqual<z.infer<typeof EmptyFoodEnum>, never>(true);
});
test("error map in extract/exclude", () => {
const foods = ["Pasta", "Pizza", "Tacos", "Burgers", "Salad"] as const;
const FoodEnum = z.enum(foods, {
errorMap: () => ({ message: "This is not food!" }),
});
const ItalianEnum = FoodEnum.extract(["Pasta", "Pizza"]);
const foodsError = FoodEnum.safeParse("Cucumbers");
const italianError = ItalianEnum.safeParse("Tacos");
if (!foodsError.success && !italianError.success) {
expect(foodsError.error.issues[0].message).toEqual(italianError.error.issues[0].message);
}
const UnhealthyEnum = FoodEnum.exclude(["Salad"], {
errorMap: () => ({ message: "This is not healthy food!" }),
});
const unhealthyError = UnhealthyEnum.safeParse("Salad");
if (!unhealthyError.success) {
expect(unhealthyError.error.issues[0].message).toEqual("This is not healthy food!");
}
});
test("readonly in ZodEnumDef", () => {
let _t!: z.ZodEnumDef<readonly ["a", "b"]>;
_t;
});
@@ -0,0 +1,551 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
import { ZodError, ZodIssueCode } from "../ZodError.js";
import { ZodParsedType } from "../helpers/util.js";
test("error creation", () => {
const err1 = ZodError.create([]);
err1.addIssue({
code: ZodIssueCode.invalid_type,
expected: ZodParsedType.object,
received: ZodParsedType.string,
path: [],
message: "",
fatal: true,
});
err1.isEmpty;
const err2 = ZodError.create(err1.issues);
const err3 = new ZodError([]);
err3.addIssues(err1.issues);
err3.addIssue(err1.issues[0]);
err1.message;
err2.message;
err3.message;
});
const errorMap: z.ZodErrorMap = (error, ctx) => {
if (error.code === ZodIssueCode.invalid_type) {
if (error.expected === "string") {
return { message: "bad type!" };
}
}
if (error.code === ZodIssueCode.custom) {
return { message: `less-than-${error.params?.minimum}` };
}
return { message: ctx.defaultError };
};
test("type error with custom error map", () => {
try {
z.string().parse(234, { errorMap });
} catch (err) {
const zerr: z.ZodError = err as any;
expect(zerr.issues[0].code).toEqual(z.ZodIssueCode.invalid_type);
expect(zerr.issues[0].message).toEqual(`bad type!`);
}
});
test("refinement fail with params", () => {
try {
z.number()
.refine((val) => val >= 3, {
params: { minimum: 3 },
})
.parse(2, { errorMap });
} catch (err) {
const zerr: z.ZodError = err as any;
expect(zerr.issues[0].code).toEqual(z.ZodIssueCode.custom);
expect(zerr.issues[0].message).toEqual(`less-than-3`);
}
});
test("custom error with custom errormap", () => {
try {
z.string()
.refine((val) => val.length > 12, {
params: { minimum: 13 },
message: "override",
})
.parse("asdf", { errorMap });
} catch (err) {
const zerr: z.ZodError = err as any;
expect(zerr.issues[0].message).toEqual("override");
}
});
test("default error message", () => {
try {
z.number()
.refine((x) => x > 3)
.parse(2);
} catch (err) {
const zerr: z.ZodError = err as any;
expect(zerr.issues.length).toEqual(1);
expect(zerr.issues[0].message).toEqual("Invalid input");
}
});
test("override error in refine", () => {
try {
z.number()
.refine((x) => x > 3, "override")
.parse(2);
} catch (err) {
const zerr: z.ZodError = err as any;
expect(zerr.issues.length).toEqual(1);
expect(zerr.issues[0].message).toEqual("override");
}
});
test("override error in refinement", () => {
try {
z.number()
.refine((x) => x > 3, {
message: "override",
})
.parse(2);
} catch (err) {
const zerr: z.ZodError = err as any;
expect(zerr.issues.length).toEqual(1);
expect(zerr.issues[0].message).toEqual("override");
}
});
test("array minimum", () => {
try {
z.array(z.string()).min(3, "tooshort").parse(["asdf", "qwer"]);
} catch (err) {
const zerr: ZodError = err as any;
expect(zerr.issues[0].code).toEqual(ZodIssueCode.too_small);
expect(zerr.issues[0].message).toEqual("tooshort");
}
try {
z.array(z.string()).min(3).parse(["asdf", "qwer"]);
} catch (err) {
const zerr: ZodError = err as any;
expect(zerr.issues[0].code).toEqual(ZodIssueCode.too_small);
expect(zerr.issues[0].message).toEqual(`Array must contain at least 3 element(s)`);
}
});
// implement test for semi-smart union logic that checks for type error on either left or right
// test("union smart errors", () => {
// // expect.assertions(2);
// const p1 = z
// .union([z.string(), z.number().refine((x) => x > 0)])
// .safeParse(-3.2);
// if (p1.success === true) throw new Error();
// expect(p1.success).toBe(false);
// expect(p1.error.issues[0].code).toEqual(ZodIssueCode.custom);
// const p2 = z.union([z.string(), z.number()]).safeParse(false);
// // .catch(err => expect(err.issues[0].code).toEqual(ZodIssueCode.invalid_union));
// if (p2.success === true) throw new Error();
// expect(p2.success).toBe(false);
// expect(p2.error.issues[0].code).toEqual(ZodIssueCode.invalid_union);
// });
test("custom path in custom error map", () => {
const schema = z.object({
items: z.array(z.string()).refine((data) => data.length > 3, {
path: ["items-too-few"],
}),
});
const errorMap: z.ZodErrorMap = (error) => {
expect(error.path.length).toBe(2);
return { message: "doesnt matter" };
};
const result = schema.safeParse({ items: ["first"] }, { errorMap });
expect(result.success).toEqual(false);
if (!result.success) {
expect(result.error.issues[0].path).toEqual(["items", "items-too-few"]);
}
});
test("error metadata from value", () => {
const dynamicRefine = z.string().refine(
(val) => val === val.toUpperCase(),
(val) => ({ params: { val } })
);
const result = dynamicRefine.safeParse("asdf");
expect(result.success).toEqual(false);
if (!result.success) {
const sub = result.error.issues[0];
expect(result.error.issues[0].code).toEqual("custom");
if (sub.code === "custom") {
expect(sub.params!.val).toEqual("asdf");
}
}
});
// test("don't call refine after validation failed", () => {
// const asdf = z
// .union([
// z.number(),
// z.string().transform(z.number(), (val) => {
// return parseFloat(val);
// }),
// ])
// .refine((v) => v >= 1);
// expect(() => asdf.safeParse("foo")).not.toThrow();
// });
test("root level formatting", () => {
const schema = z.string().email();
const result = schema.safeParse("asdfsdf");
expect(result.success).toEqual(false);
if (!result.success) {
expect(result.error.format()._errors).toEqual(["Invalid email"]);
}
});
test("custom path", () => {
const schema = z
.object({
password: z.string(),
confirm: z.string(),
})
.refine((val) => val.confirm === val.password, { path: ["confirm"] });
const result = schema.safeParse({
password: "peanuts",
confirm: "qeanuts",
});
expect(result.success).toEqual(false);
if (!result.success) {
// nested errors
const error = result.error.format();
expect(error._errors).toEqual([]);
expect(error.password?._errors).toEqual(undefined);
expect(error.confirm?._errors).toEqual(["Invalid input"]);
}
});
test("custom path", () => {
const schema = z
.object({
password: z.string().min(6),
confirm: z.string().min(6),
})
.refine((val) => val.confirm === val.password);
const result = schema.safeParse({
password: "qwer",
confirm: "asdf",
});
expect(result.success).toEqual(false);
if (!result.success) {
expect(result.error.issues.length).toEqual(3);
}
});
const schema = z.object({
inner: z.object({
name: z
.string()
.refine((val) => val.length > 5)
.array()
.refine((val) => val.length <= 1),
}),
});
test("no abort early on refinements", () => {
const invalidItem = {
inner: { name: ["aasd", "asdfasdfasfd"] },
};
const result1 = schema.safeParse(invalidItem);
expect(result1.success).toEqual(false);
if (!result1.success) {
expect(result1.error.issues.length).toEqual(2);
}
});
test("formatting", () => {
const invalidItem = {
inner: { name: ["aasd", "asdfasdfasfd"] },
};
const invalidArray = {
inner: { name: ["asdfasdf", "asdfasdfasfd"] },
};
const result1 = schema.safeParse(invalidItem);
const result2 = schema.safeParse(invalidArray);
expect(result1.success).toEqual(false);
expect(result2.success).toEqual(false);
if (!result1.success) {
const error = result1.error.format();
expect(error._errors).toEqual([]);
expect(error.inner?._errors).toEqual([]);
// expect(error.inner?.name?._errors).toEqual(["Invalid input"]);
// expect(error.inner?.name?.[0]._errors).toEqual(["Invalid input"]);
expect(error.inner?.name?.[1]).toEqual(undefined);
}
if (!result2.success) {
type FormattedError = z.inferFormattedError<typeof schema>;
const error: FormattedError = result2.error.format();
expect(error._errors).toEqual([]);
expect(error.inner?._errors).toEqual([]);
expect(error.inner?.name?._errors).toEqual(["Invalid input"]);
expect(error.inner?.name?.[0]).toEqual(undefined);
expect(error.inner?.name?.[1]).toEqual(undefined);
expect(error.inner?.name?.[2]).toEqual(undefined);
}
// test custom mapper
if (!result2.success) {
type FormattedError = z.inferFormattedError<typeof schema, number>;
const error: FormattedError = result2.error.format(() => 5);
expect(error._errors).toEqual([]);
expect(error.inner?._errors).toEqual([]);
expect(error.inner?.name?._errors).toEqual([5]);
}
});
test("formatting with nullable and optional fields", () => {
const nameSchema = z.string().refine((val) => val.length > 5);
const schema = z.object({
nullableObject: z.object({ name: nameSchema }).nullable(),
nullableArray: z.array(nameSchema).nullable(),
nullableTuple: z.tuple([nameSchema, nameSchema, z.number()]).nullable(),
optionalObject: z.object({ name: nameSchema }).optional(),
optionalArray: z.array(nameSchema).optional(),
optionalTuple: z.tuple([nameSchema, nameSchema, z.number()]).optional(),
});
const invalidItem = {
nullableObject: { name: "abcd" },
nullableArray: ["abcd"],
nullableTuple: ["abcd", "abcd", 1],
optionalObject: { name: "abcd" },
optionalArray: ["abcd"],
optionalTuple: ["abcd", "abcd", 1],
};
const result = schema.safeParse(invalidItem);
expect(result.success).toEqual(false);
if (!result.success) {
type FormattedError = z.inferFormattedError<typeof schema>;
const error: FormattedError = result.error.format();
expect(error._errors).toEqual([]);
expect(error.nullableObject?._errors).toEqual([]);
expect(error.nullableObject?.name?._errors).toEqual(["Invalid input"]);
expect(error.nullableArray?._errors).toEqual([]);
expect(error.nullableArray?.[0]?._errors).toEqual(["Invalid input"]);
expect(error.nullableTuple?._errors).toEqual([]);
expect(error.nullableTuple?.[0]?._errors).toEqual(["Invalid input"]);
expect(error.nullableTuple?.[1]?._errors).toEqual(["Invalid input"]);
expect(error.optionalObject?._errors).toEqual([]);
expect(error.optionalObject?.name?._errors).toEqual(["Invalid input"]);
expect(error.optionalArray?._errors).toEqual([]);
expect(error.optionalArray?.[0]?._errors).toEqual(["Invalid input"]);
expect(error.optionalTuple?._errors).toEqual([]);
expect(error.optionalTuple?.[0]?._errors).toEqual(["Invalid input"]);
expect(error.optionalTuple?.[1]?._errors).toEqual(["Invalid input"]);
}
});
const stringWithCustomError = z.string({
errorMap: (issue, ctx) => ({
message: issue.code === "invalid_type" ? (ctx.data ? "Invalid name" : "Name is required") : ctx.defaultError,
}),
});
test("schema-bound error map", () => {
const result = stringWithCustomError.safeParse(1234);
expect(result.success).toEqual(false);
if (!result.success) {
expect(result.error.issues[0].message).toEqual("Invalid name");
}
const result2 = stringWithCustomError.safeParse(undefined);
expect(result2.success).toEqual(false);
if (!result2.success) {
expect(result2.error.issues[0].message).toEqual("Name is required");
}
// support contextual override
const result3 = stringWithCustomError.safeParse(undefined, {
errorMap: () => ({ message: "OVERRIDE" }),
});
expect(result3.success).toEqual(false);
if (!result3.success) {
expect(result3.error.issues[0].message).toEqual("OVERRIDE");
}
});
test("overrideErrorMap", () => {
// support overrideErrorMap
z.setErrorMap(() => ({ message: "OVERRIDE" }));
const result4 = stringWithCustomError.min(10).safeParse("tooshort");
expect(result4.success).toEqual(false);
if (!result4.success) {
expect(result4.error.issues[0].message).toEqual("OVERRIDE");
}
z.setErrorMap(z.defaultErrorMap);
});
test("invalid and required", () => {
const str = z.string({
invalid_type_error: "Invalid name",
required_error: "Name is required",
});
const result1 = str.safeParse(1234);
expect(result1.success).toEqual(false);
if (!result1.success) {
expect(result1.error.issues[0].message).toEqual("Invalid name");
}
const result2 = str.safeParse(undefined);
expect(result2.success).toEqual(false);
if (!result2.success) {
expect(result2.error.issues[0].message).toEqual("Name is required");
}
});
test("Fallback to default required error", () => {
const str = z.string({
invalid_type_error: "Invalid name",
// required_error: "Name is required",
});
const result2 = str.safeParse(undefined);
expect(result2.success).toEqual(false);
if (!result2.success) {
expect(result2.error.issues[0].message).toEqual("Required");
}
});
test("invalid and required and errorMap", () => {
expect(() => {
return z.string({
invalid_type_error: "Invalid name",
required_error: "Name is required",
errorMap: () => ({ message: "OVERRIDE" }),
});
}).toThrow();
});
test("strict error message", () => {
const errorMsg = "Invalid object";
const obj = z.object({ x: z.string() }).strict(errorMsg);
const result = obj.safeParse({ x: "a", y: "b" });
expect(result.success).toEqual(false);
if (!result.success) {
expect(result.error.issues[0].message).toEqual(errorMsg);
}
});
test("enum error message, invalid enum elementstring", () => {
try {
z.enum(["Tuna", "Trout"]).parse("Salmon");
} catch (err) {
const zerr: z.ZodError = err as any;
expect(zerr.issues.length).toEqual(1);
expect(zerr.issues[0].message).toEqual("Invalid enum value. Expected 'Tuna' | 'Trout', received 'Salmon'");
}
});
test("enum error message, invalid type", () => {
try {
z.enum(["Tuna", "Trout"]).parse(12);
} catch (err) {
const zerr: z.ZodError = err as any;
expect(zerr.issues.length).toEqual(1);
expect(zerr.issues[0].message).toEqual("Expected 'Tuna' | 'Trout', received number");
}
});
test("nativeEnum default error message", () => {
enum Fish {
Tuna = "Tuna",
Trout = "Trout",
}
try {
z.nativeEnum(Fish).parse("Salmon");
} catch (err) {
const zerr: z.ZodError = err as any;
expect(zerr.issues.length).toEqual(1);
expect(zerr.issues[0].message).toEqual("Invalid enum value. Expected 'Tuna' | 'Trout', received 'Salmon'");
}
});
test("literal default error message", () => {
try {
z.literal("Tuna").parse("Trout");
} catch (err) {
const zerr: z.ZodError = err as any;
expect(zerr.issues.length).toEqual(1);
expect(zerr.issues[0].message).toEqual(`Invalid literal value, expected "Tuna"`);
}
});
test("literal bigint default error message", () => {
try {
z.literal(BigInt(12)).parse(BigInt(13));
} catch (err) {
const zerr: z.ZodError = err as any;
expect(zerr.issues.length).toEqual(1);
expect(zerr.issues[0].message).toEqual(`Invalid literal value, expected "12"`);
}
});
test("enum with message returns the custom error message", () => {
const schema = z.enum(["apple", "banana"], {
message: "the value provided is invalid",
});
const result1 = schema.safeParse("berries");
expect(result1.success).toEqual(false);
if (!result1.success) {
expect(result1.error.issues[0].message).toEqual("the value provided is invalid");
}
const result2 = schema.safeParse(undefined);
expect(result2.success).toEqual(false);
if (!result2.success) {
expect(result2.error.issues[0].message).toEqual("the value provided is invalid");
}
const result3 = schema.safeParse("banana");
expect(result3.success).toEqual(true);
const result4 = schema.safeParse(null);
expect(result4.success).toEqual(false);
if (!result4.success) {
expect(result4.error.issues[0].message).toEqual("the value provided is invalid");
}
});
test("when the message is falsy, it is used as is provided", () => {
const schema = z.string().max(1, { message: "" });
const result = schema.safeParse("asdf");
expect(result.success).toEqual(false);
if (!result.success) {
expect(result.error.issues[0].message).toEqual("");
}
});
// test("dont short circuit on continuable errors", () => {
// const user = z
// .object({
// password: z.string().min(6),
// confirm: z.string(),
// })
// .refine((data) => data.password === data.confirm, {
// message: "Passwords don't match",
// path: ["confirm"],
// });
// const result = user.safeParse({ password: "asdf", confirm: "qwer" });
// if (!result.success) {
// expect(result.error.issues.length).toEqual(2);
// }
// });
@@ -0,0 +1,87 @@
// @ts-ignore TS6133
import { test } from "vitest";
import * as z from "zod/v3";
import { util } from "../helpers/util.js";
test("first party switch", () => {
const myType = z.string() as z.ZodFirstPartySchemaTypes;
const def = myType._def;
switch (def.typeName) {
case z.ZodFirstPartyTypeKind.ZodString:
break;
case z.ZodFirstPartyTypeKind.ZodNumber:
break;
case z.ZodFirstPartyTypeKind.ZodNaN:
break;
case z.ZodFirstPartyTypeKind.ZodBigInt:
break;
case z.ZodFirstPartyTypeKind.ZodBoolean:
break;
case z.ZodFirstPartyTypeKind.ZodDate:
break;
case z.ZodFirstPartyTypeKind.ZodUndefined:
break;
case z.ZodFirstPartyTypeKind.ZodNull:
break;
case z.ZodFirstPartyTypeKind.ZodAny:
break;
case z.ZodFirstPartyTypeKind.ZodUnknown:
break;
case z.ZodFirstPartyTypeKind.ZodNever:
break;
case z.ZodFirstPartyTypeKind.ZodVoid:
break;
case z.ZodFirstPartyTypeKind.ZodArray:
break;
case z.ZodFirstPartyTypeKind.ZodObject:
break;
case z.ZodFirstPartyTypeKind.ZodUnion:
break;
case z.ZodFirstPartyTypeKind.ZodDiscriminatedUnion:
break;
case z.ZodFirstPartyTypeKind.ZodIntersection:
break;
case z.ZodFirstPartyTypeKind.ZodTuple:
break;
case z.ZodFirstPartyTypeKind.ZodRecord:
break;
case z.ZodFirstPartyTypeKind.ZodMap:
break;
case z.ZodFirstPartyTypeKind.ZodSet:
break;
case z.ZodFirstPartyTypeKind.ZodFunction:
break;
case z.ZodFirstPartyTypeKind.ZodLazy:
break;
case z.ZodFirstPartyTypeKind.ZodLiteral:
break;
case z.ZodFirstPartyTypeKind.ZodEnum:
break;
case z.ZodFirstPartyTypeKind.ZodEffects:
break;
case z.ZodFirstPartyTypeKind.ZodNativeEnum:
break;
case z.ZodFirstPartyTypeKind.ZodOptional:
break;
case z.ZodFirstPartyTypeKind.ZodNullable:
break;
case z.ZodFirstPartyTypeKind.ZodDefault:
break;
case z.ZodFirstPartyTypeKind.ZodCatch:
break;
case z.ZodFirstPartyTypeKind.ZodPromise:
break;
case z.ZodFirstPartyTypeKind.ZodBranded:
break;
case z.ZodFirstPartyTypeKind.ZodPipeline:
break;
case z.ZodFirstPartyTypeKind.ZodSymbol:
break;
case z.ZodFirstPartyTypeKind.ZodReadonly:
break;
default:
util.assertNever(def);
}
});
@@ -0,0 +1,21 @@
// @ts-ignore TS6133
import { test } from "vitest";
import type { ZodFirstPartySchemaTypes, ZodFirstPartyTypeKind } from "zod/v3";
import { util } from "../helpers/util.js";
test("Identify missing [ZodFirstPartySchemaTypes]", () => {
type ZodFirstPartySchemaForType<T extends ZodFirstPartyTypeKind> = ZodFirstPartySchemaTypes extends infer Schema
? Schema extends { _def: { typeName: T } }
? Schema
: never
: never;
type ZodMappedTypes = {
[key in ZodFirstPartyTypeKind]: ZodFirstPartySchemaForType<key>;
};
type ZodFirstPartySchemaTypesMissingFromUnion = keyof {
[key in keyof ZodMappedTypes as ZodMappedTypes[key] extends { _def: never } ? key : never]: unknown;
};
util.assertEqual<ZodFirstPartySchemaTypesMissingFromUnion, never>(true);
});
@@ -0,0 +1,261 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
import { util } from "../helpers/util.js";
const args1 = z.tuple([z.string()]);
const returns1 = z.number();
const func1 = z.function(args1, returns1);
test("function parsing", () => {
const parsed = func1.parse((arg: any) => arg.length);
const result = parsed("asdf");
expect(result).toBe(4);
});
test("parsed function fail 1", () => {
const parsed = func1.parse((x: string) => x);
expect(() => parsed("asdf")).toThrow();
});
test("parsed function fail 2", () => {
const parsed = func1.parse((x: string) => x);
expect(() => parsed(13 as any)).toThrow();
});
test("function inference 1", () => {
type func1 = z.TypeOf<typeof func1>;
util.assertEqual<func1, (k: string) => number>(true);
});
test("method parsing", () => {
const methodObject = z.object({
property: z.number(),
method: z.function().args(z.string()).returns(z.number()),
});
const methodInstance = {
property: 3,
method: function (s: string) {
return s.length + this.property;
},
};
const parsed = methodObject.parse(methodInstance);
expect(parsed.method("length=8")).toBe(11); // 8 length + 3 property
});
test("async method parsing", async () => {
const methodObject = z.object({
property: z.number(),
method: z.function().args(z.string()).returns(z.promise(z.number())),
});
const methodInstance = {
property: 3,
method: async function (s: string) {
return s.length + this.property;
},
};
const parsed = methodObject.parse(methodInstance);
expect(await parsed.method("length=8")).toBe(11); // 8 length + 3 property
});
test("args method", () => {
const t1 = z.function();
type t1 = z.infer<typeof t1>;
util.assertEqual<t1, (...args_1: unknown[]) => unknown>(true);
const t2 = t1.args(z.string());
type t2 = z.infer<typeof t2>;
util.assertEqual<t2, (arg: string, ...args_1: unknown[]) => unknown>(true);
const t3 = t2.returns(z.boolean());
type t3 = z.infer<typeof t3>;
util.assertEqual<t3, (arg: string, ...args_1: unknown[]) => boolean>(true);
});
const args2 = z.tuple([
z.object({
f1: z.number(),
f2: z.string().nullable(),
f3: z.array(z.boolean().optional()).optional(),
}),
]);
const returns2 = z.union([z.string(), z.number()]);
const func2 = z.function(args2, returns2);
test("function inference 2", () => {
type func2 = z.TypeOf<typeof func2>;
util.assertEqual<
func2,
(arg: {
f1: number;
f2: string | null;
f3?: (boolean | undefined)[] | undefined;
}) => string | number
>(true);
});
test("valid function run", () => {
const validFunc2Instance = func2.validate((_x) => {
return "adf" as any;
});
const checker = () => {
validFunc2Instance({
f1: 21,
f2: "asdf",
f3: [true, false],
});
};
checker();
});
test("input validation error", () => {
const invalidFuncInstance = func2.validate((_x) => {
return "adf" as any;
});
const checker = () => {
invalidFuncInstance("Invalid_input" as any);
};
expect(checker).toThrow();
});
test("output validation error", () => {
const invalidFuncInstance = func2.validate((_x) => {
return ["this", "is", "not", "valid", "output"] as any;
});
const checker = () => {
invalidFuncInstance({
f1: 21,
f2: "asdf",
f3: [true, false],
});
};
expect(checker).toThrow();
});
z.function(z.tuple([z.string()])).args()._def.args;
test("special function error codes", () => {
const checker = z.function(z.tuple([z.string()]), z.boolean()).implement((arg) => {
return arg.length as any;
});
try {
checker("12" as any);
} catch (err) {
const zerr = err as z.ZodError;
const first = zerr.issues[0];
if (first.code !== z.ZodIssueCode.invalid_return_type) throw new Error();
expect(first.returnTypeError).toBeInstanceOf(z.ZodError);
}
try {
checker(12 as any);
} catch (err) {
const zerr = err as z.ZodError;
const first = zerr.issues[0];
if (first.code !== z.ZodIssueCode.invalid_arguments) throw new Error();
expect(first.argumentsError).toBeInstanceOf(z.ZodError);
}
});
test("function with async refinements", async () => {
const func = z
.function()
.args(z.string().refine(async (val) => val.length > 10))
.returns(z.promise(z.number().refine(async (val) => val > 10)))
.implement(async (val) => {
return val.length;
});
const results = [];
try {
await func("asdfasdf");
results.push("success");
} catch (_err) {
results.push("fail");
}
try {
await func("asdflkjasdflkjsf");
results.push("success");
} catch (_err) {
results.push("fail");
}
expect(results).toEqual(["fail", "success"]);
});
test("non async function with async refinements should fail", async () => {
const func = z
.function()
.args(z.string().refine(async (val) => val.length > 10))
.returns(z.number().refine(async (val) => val > 10))
.implement((val) => {
return val.length;
});
const results = [];
try {
await func("asdasdfasdffasdf");
results.push("success");
} catch (_err) {
results.push("fail");
}
expect(results).toEqual(["fail"]);
});
test("allow extra parameters", () => {
const maxLength5 = z
.function()
.args(z.string())
.returns(z.boolean())
.implement((str, _arg, _qewr) => {
return str.length <= 5;
});
const filteredList = ["apple", "orange", "pear", "banana", "strawberry"].filter(maxLength5);
expect(filteredList.length).toEqual(2);
});
test("params and returnType getters", () => {
const func = z.function().args(z.string()).returns(z.string());
const paramResult = func.parameters().items[0].parse("asdf");
expect(paramResult).toBe("asdf");
const returnResult = func.returnType().parse("asdf");
expect(returnResult).toBe("asdf");
});
test("inference with transforms", () => {
const funcSchema = z
.function()
.args(z.string().transform((val) => val.length))
.returns(z.object({ val: z.number() }));
const myFunc = funcSchema.implement((val) => {
return { val, extra: "stuff" };
});
myFunc("asdf");
util.assertEqual<typeof myFunc, (arg: string, ...args_1: unknown[]) => { val: number; extra: string }>(true);
});
test("fallback to OuterTypeOfFunction", () => {
const funcSchema = z
.function()
.args(z.string().transform((val) => val.length))
.returns(z.object({ arg: z.number() }).transform((val) => val.arg));
const myFunc = funcSchema.implement((val) => {
return { arg: val, arg2: false };
});
util.assertEqual<typeof myFunc, (arg: string, ...args_1: unknown[]) => number>(true);
});
@@ -0,0 +1,48 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
import { util } from "../helpers/util.js";
test("generics", () => {
async function stripOuter<TData extends z.ZodTypeAny>(schema: TData, data: unknown) {
return z
.object({
nested: schema, // as z.ZodTypeAny,
})
.transform((data) => {
return data.nested!;
})
.parse({ nested: data });
}
const result = stripOuter(z.object({ a: z.string() }), { a: "asdf" });
util.assertEqual<typeof result, Promise<{ a: string }>>(true);
});
// test("assignability", () => {
// const createSchemaAndParse = <K extends string, VS extends z.ZodString>(
// key: K,
// valueSchema: VS,
// data: unknown
// ) => {
// const schema = z.object({
// [key]: valueSchema,
// } as { [k in K]: VS });
// return { [key]: valueSchema };
// const parsed = schema.parse(data);
// return parsed;
// // const inferred: z.infer<z.ZodObject<{ [k in K]: VS }>> = parsed;
// // return inferred;
// };
// const parsed = createSchemaAndParse("foo", z.string(), { foo: "" });
// util.assertEqual<typeof parsed, { foo: string }>(true);
// });
test("nested no undefined", () => {
const inner = z.string().or(z.array(z.string()));
const outer = z.object({ inner });
type outerSchema = z.infer<typeof outer>;
z.util.assertEqual<outerSchema, { inner: string | string[] }>(true);
expect(outer.safeParse({ inner: undefined }).success).toEqual(false);
});
@@ -0,0 +1,37 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
import { util } from "../helpers/util.js";
test("instanceof", async () => {
class Test {}
class Subtest extends Test {}
abstract class AbstractBar {
constructor(public val: string) {}
}
class Bar extends AbstractBar {}
const TestSchema = z.instanceof(Test);
const SubtestSchema = z.instanceof(Subtest);
const AbstractSchema = z.instanceof(AbstractBar);
const BarSchema = z.instanceof(Bar);
TestSchema.parse(new Test());
TestSchema.parse(new Subtest());
SubtestSchema.parse(new Subtest());
AbstractSchema.parse(new Bar("asdf"));
const bar = BarSchema.parse(new Bar("asdf"));
expect(bar.val).toEqual("asdf");
await expect(() => SubtestSchema.parse(new Test())).toThrow(/Input not instance of Subtest/);
await expect(() => TestSchema.parse(12)).toThrow(/Input not instance of Test/);
util.assertEqual<Test, z.infer<typeof TestSchema>>(true);
});
test("instanceof fatal", () => {
const schema = z.instanceof(Date).refine((d) => d.toString());
const res = schema.safeParse(null);
expect(res.success).toBe(false);
});
@@ -0,0 +1,110 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
test("object intersection", () => {
const BaseTeacher = z.object({
subjects: z.array(z.string()),
});
const HasID = z.object({ id: z.string() });
const Teacher = z.intersection(BaseTeacher.passthrough(), HasID); // BaseTeacher.merge(HasID);
const data = {
subjects: ["math"],
id: "asdfasdf",
};
expect(Teacher.parse(data)).toEqual(data);
expect(() => Teacher.parse({ subject: data.subjects })).toThrow();
expect(Teacher.parse({ ...data, extra: 12 })).toEqual({ ...data, extra: 12 });
expect(() => z.intersection(BaseTeacher.strict(), HasID).parse({ ...data, extra: 12 })).toThrow();
});
test("deep intersection", () => {
const Animal = z.object({
properties: z.object({
is_animal: z.boolean(),
}),
});
const Cat = z
.object({
properties: z.object({
jumped: z.boolean(),
}),
})
.and(Animal);
type _Cat = z.infer<typeof Cat>;
// const cat:Cat = 'asdf' as any;
const cat = Cat.parse({ properties: { is_animal: true, jumped: true } });
expect(cat.properties).toEqual({ is_animal: true, jumped: true });
});
test("deep intersection of arrays", async () => {
const Author = z.object({
posts: z.array(
z.object({
post_id: z.number(),
})
),
});
const Registry = z
.object({
posts: z.array(
z.object({
title: z.string(),
})
),
})
.and(Author);
const posts = [
{ post_id: 1, title: "Novels" },
{ post_id: 2, title: "Fairy tales" },
];
const cat = Registry.parse({ posts });
expect(cat.posts).toEqual(posts);
const asyncCat = await Registry.parseAsync({ posts });
expect(asyncCat.posts).toEqual(posts);
});
test("invalid intersection types", async () => {
const numberIntersection = z.intersection(
z.number(),
z.number().transform((x) => x + 1)
);
const syncResult = numberIntersection.safeParse(1234);
expect(syncResult.success).toEqual(false);
if (!syncResult.success) {
expect(syncResult.error.issues[0].code).toEqual(z.ZodIssueCode.invalid_intersection_types);
}
const asyncResult = await numberIntersection.spa(1234);
expect(asyncResult.success).toEqual(false);
if (!asyncResult.success) {
expect(asyncResult.error.issues[0].code).toEqual(z.ZodIssueCode.invalid_intersection_types);
}
});
test("invalid array merge", async () => {
const stringArrInt = z.intersection(
z.string().array(),
z
.string()
.array()
.transform((val) => [...val, "asdf"])
);
const syncResult = stringArrInt.safeParse(["asdf", "qwer"]);
expect(syncResult.success).toEqual(false);
if (!syncResult.success) {
expect(syncResult.error.issues[0].code).toEqual(z.ZodIssueCode.invalid_intersection_types);
}
const asyncResult = await stringArrInt.spa(["asdf", "qwer"]);
expect(asyncResult.success).toEqual(false);
if (!asyncResult.success) {
expect(asyncResult.error.issues[0].code).toEqual(z.ZodIssueCode.invalid_intersection_types);
}
});
@@ -0,0 +1,76 @@
import * as z from "zod/v3";
export const filePath = __filename;
// z.object()
export const Test = z.object({
f1: z.number(),
});
export type Test = z.infer<typeof Test>;
export const instanceOfTest: Test = {
f1: 1,
};
// z.object().merge()
export const TestMerge = z
.object({
f2: z.string().optional(),
})
.merge(Test);
export type TestMerge = z.infer<typeof TestMerge>;
export const instanceOfTestMerge: TestMerge = {
f1: 1,
f2: "string",
};
// z.union()
export const TestUnion = z.union([
z.object({
f2: z.string().optional(),
}),
Test,
]);
export type TestUnion = z.infer<typeof TestUnion>;
export const instanceOfTestUnion: TestUnion = {
f1: 1,
f2: "string",
};
// z.object().partial()
export const TestPartial = Test.partial();
export type TestPartial = z.infer<typeof TestPartial>;
export const instanceOfTestPartial: TestPartial = {
f1: 1,
};
// z.object().pick()
export const TestPick = TestMerge.pick({ f1: true });
export type TestPick = z.infer<typeof TestPick>;
export const instanceOfTestPick: TestPick = {
f1: 1,
};
// z.object().omit()
export const TestOmit = TestMerge.omit({ f2: true });
export type TestOmit = z.infer<typeof TestOmit>;
export const instanceOfTestOmit: TestOmit = {
f1: 1,
};
@@ -0,0 +1,207 @@
import { test } from "vitest";
// import path from "path";
// import { Node, Project, SyntaxKind } from "ts-morph";
// import { filePath } from "./language-server.source";
// The following tool is helpful for understanding the TypeScript AST associated with these tests:
// https://ts-ast-viewer.com/ (just copy the contents of language-server.source into the viewer)
test("", () => {});
// describe("Executing Go To Definition (and therefore Find Usages and Rename Refactoring) using an IDE works on inferred object properties", () => {
// // Compile file developmentEnvironment.source
// const project = new Project({
// tsConfigFilePath: path.join(__dirname, "..", "..", "tsconfig.json"),
// skipAddingFilesFromTsConfig: true,
// });
// const sourceFile = project.addSourceFileAtPath(filePath);
// test("works for object properties inferred from z.object()", () => {
// // Find usage of Test.f1 property
// const instanceVariable =
// sourceFile.getVariableDeclarationOrThrow("instanceOfTest");
// const propertyBeingAssigned = getPropertyBeingAssigned(
// instanceVariable,
// "f1"
// );
// // Find definition of Test.f1 property
// const definitionOfProperty = propertyBeingAssigned?.getDefinitionNodes()[0];
// const parentOfProperty = definitionOfProperty?.getFirstAncestorByKind(
// SyntaxKind.VariableDeclaration
// );
// // Assert that find definition returned the Zod definition of Test
// expect(definitionOfProperty?.getText()).toEqual("f1: z.number()");
// expect(parentOfProperty?.getName()).toEqual("Test");
// });
// // test("works for first object properties inferred from z.object().merge()", () => {
// // // Find usage of TestMerge.f1 property
// // const instanceVariable = sourceFile.getVariableDeclarationOrThrow(
// // "instanceOfTestMerge"
// // );
// // const propertyBeingAssigned = getPropertyBeingAssigned(
// // instanceVariable,
// // "f1"
// // );
// // // Find definition of TestMerge.f1 property
// // const definitionOfProperty = propertyBeingAssigned?.getDefinitionNodes()[0];
// // const parentOfProperty = definitionOfProperty?.getFirstAncestorByKind(
// // SyntaxKind.VariableDeclaration
// // );
// // // Assert that find definition returned the Zod definition of Test
// // expect(definitionOfProperty?.getText()).toEqual("f1: z.number()");
// // expect(parentOfProperty?.getName()).toEqual("Test");
// // });
// // test("works for second object properties inferred from z.object().merge()", () => {
// // // Find usage of TestMerge.f2 property
// // const instanceVariable = sourceFile.getVariableDeclarationOrThrow(
// // "instanceOfTestMerge"
// // );
// // const propertyBeingAssigned = getPropertyBeingAssigned(
// // instanceVariable,
// // "f2"
// // );
// // // Find definition of TestMerge.f2 property
// // const definitionOfProperty = propertyBeingAssigned?.getDefinitionNodes()[0];
// // const parentOfProperty = definitionOfProperty?.getFirstAncestorByKind(
// // SyntaxKind.VariableDeclaration
// // );
// // // Assert that find definition returned the Zod definition of TestMerge
// // expect(definitionOfProperty?.getText()).toEqual(
// // "f2: z.string().optional()"
// // );
// // expect(parentOfProperty?.getName()).toEqual("TestMerge");
// // });
// test("works for first object properties inferred from z.union()", () => {
// // Find usage of TestUnion.f1 property
// const instanceVariable = sourceFile.getVariableDeclarationOrThrow(
// "instanceOfTestUnion"
// );
// const propertyBeingAssigned = getPropertyBeingAssigned(
// instanceVariable,
// "f1"
// );
// // Find definition of TestUnion.f1 property
// const definitionOfProperty = propertyBeingAssigned?.getDefinitionNodes()[0];
// const parentOfProperty = definitionOfProperty?.getFirstAncestorByKind(
// SyntaxKind.VariableDeclaration
// );
// // Assert that find definition returned the Zod definition of Test
// expect(definitionOfProperty?.getText()).toEqual("f1: z.number()");
// expect(parentOfProperty?.getName()).toEqual("Test");
// });
// test("works for second object properties inferred from z.union()", () => {
// // Find usage of TestUnion.f2 property
// const instanceVariable = sourceFile.getVariableDeclarationOrThrow(
// "instanceOfTestUnion"
// );
// const propertyBeingAssigned = getPropertyBeingAssigned(
// instanceVariable,
// "f2"
// );
// // Find definition of TestUnion.f2 property
// const definitionOfProperty = propertyBeingAssigned?.getDefinitionNodes()[0];
// const parentOfProperty = definitionOfProperty?.getFirstAncestorByKind(
// SyntaxKind.VariableDeclaration
// );
// // Assert that find definition returned the Zod definition of TestUnion
// expect(definitionOfProperty?.getText()).toEqual(
// "f2: z.string().optional()"
// );
// expect(parentOfProperty?.getName()).toEqual("TestUnion");
// });
// test("works for object properties inferred from z.object().partial()", () => {
// // Find usage of TestPartial.f1 property
// const instanceVariable = sourceFile.getVariableDeclarationOrThrow(
// "instanceOfTestPartial"
// );
// const propertyBeingAssigned = getPropertyBeingAssigned(
// instanceVariable,
// "f1"
// );
// // Find definition of TestPartial.f1 property
// const definitionOfProperty = propertyBeingAssigned?.getDefinitionNodes()[0];
// const parentOfProperty = definitionOfProperty?.getFirstAncestorByKind(
// SyntaxKind.VariableDeclaration
// );
// // Assert that find definition returned the Zod definition of Test
// expect(definitionOfProperty?.getText()).toEqual("f1: z.number()");
// expect(parentOfProperty?.getName()).toEqual("Test");
// });
// test("works for object properties inferred from z.object().pick()", () => {
// // Find usage of TestPick.f1 property
// const instanceVariable =
// sourceFile.getVariableDeclarationOrThrow("instanceOfTestPick");
// const propertyBeingAssigned = getPropertyBeingAssigned(
// instanceVariable,
// "f1"
// );
// // Find definition of TestPick.f1 property
// const definitionOfProperty = propertyBeingAssigned?.getDefinitionNodes()[0];
// const parentOfProperty = definitionOfProperty?.getFirstAncestorByKind(
// SyntaxKind.VariableDeclaration
// );
// // Assert that find definition returned the Zod definition of Test
// expect(definitionOfProperty?.getText()).toEqual("f1: z.number()");
// expect(parentOfProperty?.getName()).toEqual("Test");
// });
// test("works for object properties inferred from z.object().omit()", () => {
// // Find usage of TestOmit.f1 property
// const instanceVariable =
// sourceFile.getVariableDeclarationOrThrow("instanceOfTestOmit");
// const propertyBeingAssigned = getPropertyBeingAssigned(
// instanceVariable,
// "f1"
// );
// // Find definition of TestOmit.f1 property
// const definitionOfProperty = propertyBeingAssigned?.getDefinitionNodes()[0];
// const parentOfProperty = definitionOfProperty?.getFirstAncestorByKind(
// SyntaxKind.VariableDeclaration
// );
// // Assert that find definition returned the Zod definition of Test
// expect(definitionOfProperty?.getText()).toEqual("f1: z.number()");
// expect(parentOfProperty?.getName()).toEqual("Test");
// });
// });
// const getPropertyBeingAssigned = (node: Node, name: string) => {
// const propertyAssignment = node.forEachDescendant((descendent) =>
// Node.isPropertyAssignment(descendent) && descendent.getName() == name
// ? descendent
// : undefined
// );
// if (propertyAssignment == null)
// fail(`Could not find property assignment with name ${name}`);
// const propertyLiteral = propertyAssignment.getFirstDescendantByKind(
// SyntaxKind.Identifier
// );
// if (propertyLiteral == null)
// fail(`Could not find property literal with name ${name}`);
// return propertyLiteral;
// };
@@ -0,0 +1,36 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
const literalTuna = z.literal("tuna");
const literalFortyTwo = z.literal(42);
const literalTrue = z.literal(true);
const terrificSymbol = Symbol("terrific");
const literalTerrificSymbol = z.literal(terrificSymbol);
test("passing validations", () => {
literalTuna.parse("tuna");
literalFortyTwo.parse(42);
literalTrue.parse(true);
literalTerrificSymbol.parse(terrificSymbol);
});
test("failing validations", () => {
expect(() => literalTuna.parse("shark")).toThrow();
expect(() => literalFortyTwo.parse(43)).toThrow();
expect(() => literalTrue.parse(false)).toThrow();
expect(() => literalTerrificSymbol.parse(Symbol("terrific"))).toThrow();
});
test("invalid_literal should have `received` field with data", () => {
const data = "shark";
const result = literalTuna.safeParse(data);
if (!result.success) {
const issue = result.error.issues[0];
if (issue.code === "invalid_literal") {
expect(issue.received).toBe(data);
}
}
});
@@ -0,0 +1,110 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
import { ZodIssueCode } from "zod/v3";
import { util } from "../helpers/util.js";
const stringMap = z.map(z.string(), z.string());
type stringMap = z.infer<typeof stringMap>;
test("type inference", () => {
util.assertEqual<stringMap, Map<string, string>>(true);
});
test("valid parse", () => {
const result = stringMap.safeParse(
new Map([
["first", "foo"],
["second", "bar"],
])
);
expect(result.success).toEqual(true);
if (result.success) {
expect(result.data.has("first")).toEqual(true);
expect(result.data.has("second")).toEqual(true);
expect(result.data.get("first")).toEqual("foo");
expect(result.data.get("second")).toEqual("bar");
}
});
test("valid parse async", async () => {
const result = await stringMap.spa(
new Map([
["first", "foo"],
["second", "bar"],
])
);
expect(result.success).toEqual(true);
if (result.success) {
expect(result.data.has("first")).toEqual(true);
expect(result.data.has("second")).toEqual(true);
expect(result.data.get("first")).toEqual("foo");
expect(result.data.get("second")).toEqual("bar");
}
});
test("throws when a Set is given", () => {
const result = stringMap.safeParse(new Set([]));
expect(result.success).toEqual(false);
if (result.success === false) {
expect(result.error.issues.length).toEqual(1);
expect(result.error.issues[0].code).toEqual(ZodIssueCode.invalid_type);
}
});
test("throws when the given map has invalid key and invalid input", () => {
const result = stringMap.safeParse(new Map([[42, Symbol()]]));
expect(result.success).toEqual(false);
if (result.success === false) {
expect(result.error.issues.length).toEqual(2);
expect(result.error.issues[0].code).toEqual(ZodIssueCode.invalid_type);
expect(result.error.issues[0].path).toEqual([0, "key"]);
expect(result.error.issues[1].code).toEqual(ZodIssueCode.invalid_type);
expect(result.error.issues[1].path).toEqual([0, "value"]);
}
});
test("throws when the given map has multiple invalid entries", () => {
// const result = stringMap.safeParse(new Map([[42, Symbol()]]));
const result = stringMap.safeParse(
new Map([
[1, "foo"],
["bar", 2],
] as [any, any][]) as Map<any, any>
);
// const result = stringMap.safeParse(new Map([[42, Symbol()]]));
expect(result.success).toEqual(false);
if (result.success === false) {
expect(result.error.issues.length).toEqual(2);
expect(result.error.issues[0].code).toEqual(ZodIssueCode.invalid_type);
expect(result.error.issues[0].path).toEqual([0, "key"]);
expect(result.error.issues[1].code).toEqual(ZodIssueCode.invalid_type);
expect(result.error.issues[1].path).toEqual([1, "value"]);
}
});
test("dirty", async () => {
const map = z.map(
z.string().refine((val) => val === val.toUpperCase(), {
message: "Keys must be uppercase",
}),
z.string()
);
const result = await map.spa(
new Map([
["first", "foo"],
["second", "bar"],
])
);
expect(result.success).toEqual(false);
if (!result.success) {
expect(result.error.issues.length).toEqual(2);
expect(result.error.issues[0].code).toEqual(z.ZodIssueCode.custom);
expect(result.error.issues[0].message).toEqual("Keys must be uppercase");
expect(result.error.issues[1].code).toEqual(z.ZodIssueCode.custom);
expect(result.error.issues[1].message).toEqual("Keys must be uppercase");
}
});
@@ -0,0 +1,4 @@
// @ts-ignore TS6133
import { test } from "vitest";
test("masking test", () => {});
@@ -0,0 +1,19 @@
// @ts-ignore TS6133
import { test } from "vitest";
import { Mocker } from "./Mocker.js";
test("mocker", () => {
const mocker = new Mocker();
mocker.string;
mocker.number;
mocker.boolean;
mocker.null;
mocker.undefined;
mocker.stringOptional;
mocker.stringNullable;
mocker.numberOptional;
mocker.numberNullable;
mocker.booleanOptional;
mocker.booleanNullable;
});
@@ -0,0 +1,24 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
const schema = z.nan();
test("passing validations", () => {
const result1 = schema.parse(Number.NaN);
expect(Number.isNaN(result1)).toBe(true);
const result2 = schema.parse(Number("Not a number"));
expect(Number.isNaN(result2)).toBe(true);
});
test("failing validations", () => {
expect(() => schema.parse(5)).toThrow();
expect(() => schema.parse("John")).toThrow();
expect(() => schema.parse(true)).toThrow();
expect(() => schema.parse(null)).toThrow();
expect(() => schema.parse(undefined)).toThrow();
expect(() => schema.parse({})).toThrow();
expect(() => schema.parse([])).toThrow();
});
@@ -0,0 +1,87 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
import { util } from "../helpers/util.js";
test("nativeEnum test with consts", () => {
const Fruits: { Apple: "apple"; Banana: "banana" } = {
Apple: "apple",
Banana: "banana",
};
const fruitEnum = z.nativeEnum(Fruits);
type fruitEnum = z.infer<typeof fruitEnum>;
fruitEnum.parse("apple");
fruitEnum.parse("banana");
fruitEnum.parse(Fruits.Apple);
fruitEnum.parse(Fruits.Banana);
util.assertEqual<fruitEnum, "apple" | "banana">(true);
});
test("nativeEnum test with real enum", () => {
enum Fruits {
Apple = "apple",
Banana = "banana",
}
// @ts-ignore
const fruitEnum = z.nativeEnum(Fruits);
type fruitEnum = z.infer<typeof fruitEnum>;
fruitEnum.parse("apple");
fruitEnum.parse("banana");
fruitEnum.parse(Fruits.Apple);
fruitEnum.parse(Fruits.Banana);
util.assertIs<fruitEnum extends Fruits ? true : false>(true);
});
test("nativeEnum test with const with numeric keys", () => {
const FruitValues = {
Apple: 10,
Banana: 20,
// @ts-ignore
} as const;
const fruitEnum = z.nativeEnum(FruitValues);
type fruitEnum = z.infer<typeof fruitEnum>;
fruitEnum.parse(10);
fruitEnum.parse(20);
fruitEnum.parse(FruitValues.Apple);
fruitEnum.parse(FruitValues.Banana);
util.assertEqual<fruitEnum, 10 | 20>(true);
});
test("from enum", () => {
enum Fruits {
Cantaloupe = 0,
Apple = "apple",
Banana = "banana",
}
const FruitEnum = z.nativeEnum(Fruits as any);
type _FruitEnum = z.infer<typeof FruitEnum>;
FruitEnum.parse(Fruits.Cantaloupe);
FruitEnum.parse(Fruits.Apple);
FruitEnum.parse("apple");
FruitEnum.parse(0);
expect(() => FruitEnum.parse(1)).toThrow();
expect(() => FruitEnum.parse("Apple")).toThrow();
expect(() => FruitEnum.parse("Cantaloupe")).toThrow();
});
test("from const", () => {
const Greek = {
Alpha: "a",
Beta: "b",
Gamma: 3,
// @ts-ignore
} as const;
const GreekEnum = z.nativeEnum(Greek);
type _GreekEnum = z.infer<typeof GreekEnum>;
GreekEnum.parse("a");
GreekEnum.parse("b");
GreekEnum.parse(3);
expect(() => GreekEnum.parse("v")).toThrow();
expect(() => GreekEnum.parse("Alpha")).toThrow();
expect(() => GreekEnum.parse(2)).toThrow();
expect(GreekEnum.enum.Alpha).toEqual("a");
});
@@ -0,0 +1,42 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
function checkErrors(a: z.ZodTypeAny, bad: any) {
let expected: any;
try {
a.parse(bad);
} catch (error) {
expected = (error as z.ZodError).formErrors;
}
try {
a.nullable().parse(bad);
} catch (error) {
expect((error as z.ZodError).formErrors).toEqual(expected);
}
}
test("Should have error messages appropriate for the underlying type", () => {
checkErrors(z.string().min(2), 1);
z.string().min(2).nullable().parse(null);
checkErrors(z.number().gte(2), 1);
z.number().gte(2).nullable().parse(null);
checkErrors(z.boolean(), "");
z.boolean().nullable().parse(null);
checkErrors(z.null(), null);
z.null().nullable().parse(null);
checkErrors(z.null(), {});
z.null().nullable().parse(null);
checkErrors(z.object({}), 1);
z.object({}).nullable().parse(null);
checkErrors(z.tuple([]), 1);
z.tuple([]).nullable().parse(null);
checkErrors(z.unknown(), 1);
z.unknown().nullable().parse(null);
});
test("unwrap", () => {
const unwrapped = z.string().nullable().unwrap();
expect(unwrapped).toBeInstanceOf(z.ZodString);
});
@@ -0,0 +1,176 @@
// @ts-ignore TS6133
import { expect, test } from "vitest";
import * as z from "zod/v3";
const gtFive = z.number().gt(5);
const gteFive = z.number().gte(-5).gte(5);
const minFive = z.number().min(0).min(5);
const ltFive = z.number().lte(10).lt(5);
const lteFive = z.number().lte(5);
const maxFive = z.number().max(10).max(5);
const intNum = z.number().int();
const positive = z.number().positive();
const negative = z.number().negative();
const nonpositive = z.number().nonpositive();
const nonnegative = z.number().nonnegative();
const multipleOfFive = z.number().multipleOf(5);
const multipleOfNegativeFive = z.number().multipleOf(-5);
const finite = z.number().finite();
const safe = z.number().safe();
const stepPointOne = z.number().step(0.1);
const stepPointZeroZeroZeroOne = z.number().step(0.0001);
const stepSixPointFour = z.number().step(6.4);
test("passing validations", () => {
z.number().parse(1);
z.number().parse(1.5);
z.number().parse(0);
z.number().parse(-1.5);
z.number().parse(-1);
z.number().parse(Number.POSITIVE_INFINITY);
z.number().parse(Number.NEGATIVE_INFINITY);
gtFive.parse(6);
gtFive.parse(Number.POSITIVE_INFINITY);
gteFive.parse(5);
gteFive.parse(Number.POSITIVE_INFINITY);
minFive.parse(5);
minFive.parse(Number.POSITIVE_INFINITY);
ltFive.parse(4);
ltFive.parse(Number.NEGATIVE_INFINITY);
lteFive.parse(5);
lteFive.parse(Number.NEGATIVE_INFINITY);
maxFive.parse(5);
maxFive.parse(Number.NEGATIVE_INFINITY);
intNum.parse(4);
positive.parse(1);
positive.parse(Number.POSITIVE_INFINITY);
negative.parse(-1);
negative.parse(Number.NEGATIVE_INFINITY);
nonpositive.parse(0);
nonpositive.parse(-1);
nonpositive.parse(Number.NEGATIVE_INFINITY);
nonnegative.parse(0);
nonnegative.parse(1);
nonnegative.parse(Number.POSITIVE_INFINITY);
multipleOfFive.parse(15);
multipleOfFive.parse(-15);
multipleOfNegativeFive.parse(-15);
multipleOfNegativeFive.parse(15);
finite.parse(123);
safe.parse(Number.MIN_SAFE_INTEGER);
safe.parse(Number.MAX_SAFE_INTEGER);
stepPointOne.parse(6);
stepPointOne.parse(6.1);
stepPointOne.parse(6.1);
stepSixPointFour.parse(12.8);
stepPointZeroZeroZeroOne.parse(3.01);
});
test("failing validations", () => {
expect(() => ltFive.parse(5)).toThrow();
expect(() => lteFive.parse(6)).toThrow();
expect(() => maxFive.parse(6)).toThrow();
expect(() => gtFive.parse(5)).toThrow();
expect(() => gteFive.parse(4)).toThrow();
expect(() => minFive.parse(4)).toThrow();
expect(() => intNum.parse(3.14)).toThrow();
expect(() => positive.parse(0)).toThrow();
expect(() => positive.parse(-1)).toThrow();
expect(() => negative.parse(0)).toThrow();
expect(() => negative.parse(1)).toThrow();
expect(() => nonpositive.parse(1)).toThrow();
expect(() => nonnegative.parse(-1)).toThrow();
expect(() => multipleOfFive.parse(7.5)).toThrow();
expect(() => multipleOfFive.parse(-7.5)).toThrow();
expect(() => multipleOfNegativeFive.parse(-7.5)).toThrow();
expect(() => multipleOfNegativeFive.parse(7.5)).toThrow();
expect(() => finite.parse(Number.POSITIVE_INFINITY)).toThrow();
expect(() => finite.parse(Number.NEGATIVE_INFINITY)).toThrow();
expect(() => safe.parse(Number.MIN_SAFE_INTEGER - 1)).toThrow();
expect(() => safe.parse(Number.MAX_SAFE_INTEGER + 1)).toThrow();
expect(() => stepPointOne.parse(6.11)).toThrow();
expect(() => stepPointOne.parse(6.1000000001)).toThrow();
expect(() => stepSixPointFour.parse(6.41)).toThrow();
});
test("parse NaN", () => {
expect(() => z.number().parse(Number.NaN)).toThrow();
});
test("min max getters", () => {
expect(z.number().minValue).toBeNull;
expect(ltFive.minValue).toBeNull;
expect(lteFive.minValue).toBeNull;
expect(maxFive.minValue).toBeNull;
expect(negative.minValue).toBeNull;
expect(nonpositive.minValue).toBeNull;
expect(intNum.minValue).toBeNull;
expect(multipleOfFive.minValue).toBeNull;
expect(finite.minValue).toBeNull;
expect(gtFive.minValue).toEqual(5);
expect(gteFive.minValue).toEqual(5);
expect(minFive.minValue).toEqual(5);
expect(minFive.min(10).minValue).toEqual(10);
expect(positive.minValue).toEqual(0);
expect(nonnegative.minValue).toEqual(0);
expect(safe.minValue).toEqual(Number.MIN_SAFE_INTEGER);
expect(z.number().maxValue).toBeNull;
expect(gtFive.maxValue).toBeNull;
expect(gteFive.maxValue).toBeNull;
expect(minFive.maxValue).toBeNull;
expect(positive.maxValue).toBeNull;
expect(nonnegative.maxValue).toBeNull;
expect(intNum.minValue).toBeNull;
expect(multipleOfFive.minValue).toBeNull;
expect(finite.minValue).toBeNull;
expect(ltFive.maxValue).toEqual(5);
expect(lteFive.maxValue).toEqual(5);
expect(maxFive.maxValue).toEqual(5);
expect(maxFive.max(1).maxValue).toEqual(1);
expect(negative.maxValue).toEqual(0);
expect(nonpositive.maxValue).toEqual(0);
expect(safe.maxValue).toEqual(Number.MAX_SAFE_INTEGER);
});
test("int getter", () => {
expect(z.number().isInt).toEqual(false);
expect(z.number().multipleOf(1.5).isInt).toEqual(false);
expect(gtFive.isInt).toEqual(false);
expect(gteFive.isInt).toEqual(false);
expect(minFive.isInt).toEqual(false);
expect(positive.isInt).toEqual(false);
expect(nonnegative.isInt).toEqual(false);
expect(finite.isInt).toEqual(false);
expect(ltFive.isInt).toEqual(false);
expect(lteFive.isInt).toEqual(false);
expect(maxFive.isInt).toEqual(false);
expect(negative.isInt).toEqual(false);
expect(nonpositive.isInt).toEqual(false);
expect(safe.isInt).toEqual(false);
expect(intNum.isInt).toEqual(true);
expect(multipleOfFive.isInt).toEqual(true);
});
test("finite getter", () => {
expect(z.number().isFinite).toEqual(false);
expect(gtFive.isFinite).toEqual(false);
expect(gteFive.isFinite).toEqual(false);
expect(minFive.isFinite).toEqual(false);
expect(positive.isFinite).toEqual(false);
expect(nonnegative.isFinite).toEqual(false);
expect(ltFive.isFinite).toEqual(false);
expect(lteFive.isFinite).toEqual(false);
expect(maxFive.isFinite).toEqual(false);
expect(negative.isFinite).toEqual(false);
expect(nonpositive.isFinite).toEqual(false);
expect(finite.isFinite).toEqual(true);
expect(intNum.isFinite).toEqual(true);
expect(multipleOfFive.isFinite).toEqual(true);
expect(z.number().min(5).max(10).isFinite).toEqual(true);
expect(safe.isFinite).toEqual(true);
});

Some files were not shown because too many files have changed in this diff Show More