├── .nvmrc ├── .npmrc ├── .github ├── FUNDING.yml └── workflows │ ├── release.yml │ └── ci.yml ├── src ├── v4 │ ├── internals │ │ ├── config.ts │ │ ├── context.ts │ │ ├── schemas │ │ │ ├── nan.ts │ │ │ ├── any.ts │ │ │ ├── null.ts │ │ │ ├── never.ts │ │ │ ├── void.ts │ │ │ ├── symbol.ts │ │ │ ├── unknown.ts │ │ │ ├── undefined.ts │ │ │ ├── lazy.ts │ │ │ ├── catch.ts │ │ │ ├── non-optional.ts │ │ │ ├── promise.ts │ │ │ ├── readonly.ts │ │ │ ├── boolean.ts │ │ │ ├── enum.ts │ │ │ ├── literal.ts │ │ │ ├── nullable.ts │ │ │ ├── optional.ts │ │ │ ├── default.ts │ │ │ ├── prefault.ts │ │ │ ├── custom.ts │ │ │ ├── template-literal.ts │ │ │ ├── function.ts │ │ │ ├── map.ts │ │ │ ├── date.ts │ │ │ ├── union.ts │ │ │ ├── tuple.ts │ │ │ ├── object.ts │ │ │ ├── pipe.ts │ │ │ ├── record.ts │ │ │ ├── set.ts │ │ │ ├── array.ts │ │ │ ├── big-int.ts │ │ │ ├── number.ts │ │ │ ├── string.ts │ │ │ └── checks │ │ │ │ └── string-format.ts │ │ ├── type.ts │ │ ├── utils.ts │ │ ├── random.ts │ │ └── fake.ts │ ├── index.ts │ └── fake.ts └── v3 │ ├── error.ts │ ├── zod-nan-faker.ts │ ├── zod-null-faker.ts │ ├── zod-never-faker.ts │ ├── zod-void-faker.ts │ ├── zod-symbol-faker.ts │ ├── zod-undefined-faker.ts │ ├── zod-literal-faker.ts │ ├── index.ts │ ├── zod-unknown-faker.ts │ ├── zod-lazy-faker.ts │ ├── zod-boolean-faker.ts │ ├── zod-branded-faker.ts │ ├── zod-pipe-faker.ts │ ├── zod-enum-faker.ts │ ├── zod-readonly-faker.ts │ ├── zod-native-enum-faker.ts │ ├── zod-union-faker.ts │ ├── zod-function-faker.ts │ ├── zod-nullable-faker.ts │ ├── zod-tuple-faker.ts │ ├── zod-optional-faker.ts │ ├── zod-discriminated-union-faker.ts │ ├── zod-default-faker.ts │ ├── zod-map-faker.ts │ ├── zod-type-faker.ts │ ├── zod-catch-faker.ts │ ├── zod-promise-faker.ts │ ├── utils.ts │ ├── zod-any-faker.ts │ ├── zod-record-faker.ts │ ├── zod-set-faker.ts │ ├── zod-effects-faker.ts │ ├── zod-array-faker.ts │ ├── fake.ts │ ├── zod-date-faker.ts │ ├── zod-object-faker.ts │ ├── zod-bigint-faker.ts │ ├── random.ts │ ├── zod-number-faker.ts │ └── installation.ts ├── .release-please-manifest.json ├── examples ├── v3 │ ├── src │ │ ├── vite-env.d.ts │ │ └── main.ts │ ├── .gitignore │ ├── package.json │ ├── index.html │ ├── tsconfig.json │ └── favicon.svg └── v4 │ ├── src │ ├── vite-env.d.ts │ ├── main.ts │ └── typescript.svg │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── tsconfig.json │ └── public │ └── vite.svg ├── .prettierignore ├── commitlint.config.js ├── .vscode └── settings.json ├── e2e └── issue-189 │ ├── v3 │ ├── tsconfig.json │ ├── package.json │ ├── src │ │ └── index.ts │ └── package-lock.json │ └── v4 │ ├── tsconfig.json │ ├── package.json │ ├── src │ └── index.ts │ └── package-lock.json ├── release-please-config.json ├── prettier.config.js ├── .gitignore ├── .prettierrc.json ├── codecov.yml ├── renovate.json ├── tests ├── v3 │ ├── zod-branded.test.ts │ ├── zod-pipe-faker.test.ts │ ├── fake.test.ts │ ├── util.ts │ ├── utils.test.ts │ ├── zod-never-faker.test.ts │ ├── zod-nan-faker.test.ts │ ├── zod-instanceof-faker.test.ts │ ├── zod-null-faker.test.ts │ ├── zod-custom-faker.test.ts │ ├── zod-any-faker.test.ts │ ├── zod-void-faker.test.ts │ ├── zod-symbol-faker.test.ts │ ├── zod-boolean-faker.test.ts │ ├── zod-literal-faker.test.ts │ ├── zod-undefined-faker.test.ts │ ├── zod-unknown-faker.test.ts │ ├── zod-map-faker.test.ts │ ├── zod-nullable-faker.test.ts │ ├── zod-union-faker.test.ts │ ├── zod-optional-faker.test.ts │ ├── zod-readonly-faker.test.ts │ ├── zod-record-faker.test.ts │ ├── zod-enum-faker.test.ts │ ├── zod-catch-faker.test.ts │ ├── zod-function-faker.test.ts │ ├── zod-lazy-faker.test.ts │ ├── zod-default-faker.test.ts │ ├── zod-tuple-faker.test.ts │ ├── zod-discriminated-union-faker.test.ts │ ├── zod-set-faker.test.ts │ ├── zod-native-enum-faker.test.ts │ ├── zod-effects-faker.test.ts │ ├── zod-promise-faker.test.ts │ ├── integration.test.ts │ ├── random.test.ts │ ├── zod-date-faker.test.ts │ ├── zod-array-faker.test.ts │ ├── zod-object-faker.test.ts │ └── zod-bigint-faker.test.ts └── v4 │ └── random.test.ts ├── tsconfig.json ├── LICENSE ├── vite.config.ts ├── package.json ├── CONTRIBUTING.md └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/* 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict = true 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | buy_me_a_coffee: iendeavor 2 | -------------------------------------------------------------------------------- /src/v4/internals/config.ts: -------------------------------------------------------------------------------- 1 | export const MAX_DEPTH = 10 2 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": "2.0.2" 3 | } 4 | -------------------------------------------------------------------------------- /examples/v3/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/v4/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/v3/error.ts: -------------------------------------------------------------------------------- 1 | export class ZodSchemaFakerError extends Error {} 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | CHANGELOG.md 4 | docs 5 | coverage 6 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | } 4 | -------------------------------------------------------------------------------- /src/v4/internals/context.ts: -------------------------------------------------------------------------------- 1 | export type Context = { 2 | /** 3 | * The current depth in a recursive structure. 4 | */ 5 | depth: number 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "editor.formatOnSave": true, 4 | "typescript.tsdk": "node_modules/typescript/lib" 5 | } 6 | -------------------------------------------------------------------------------- /e2e/issue-189/v3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": ".", 4 | "strict": true, 5 | "skipLibCheck": true, 6 | "noEmit": true, 7 | "module": "Node16" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /e2e/issue-189/v4/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": ".", 4 | "strict": true, 5 | "skipLibCheck": true, 6 | "noEmit": true, 7 | "module": "Node16" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/v4/index.ts: -------------------------------------------------------------------------------- 1 | export { fake } from './fake' 2 | export { getFaker, randexp, seed, setFaker } from './internals/random' 3 | export { custom } from './internals/schemas/custom' 4 | export { type Fake } from './internals/type' 5 | -------------------------------------------------------------------------------- /src/v4/fake.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { rootFake } from './internals/fake' 3 | 4 | export function fake(schema: T): core.infer { 5 | return rootFake(schema, { depth: 0 }) 6 | } 7 | -------------------------------------------------------------------------------- /src/v3/zod-nan-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { ZodTypeFaker } from './zod-type-faker' 3 | 4 | export class ZodNaNFaker extends ZodTypeFaker { 5 | fake(): z.infer { 6 | return NaN 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/v3/zod-null-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { ZodTypeFaker } from './zod-type-faker' 3 | 4 | export class ZodNullFaker extends ZodTypeFaker { 5 | fake(): z.infer { 6 | return null 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/v3/zod-never-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { ZodTypeFaker } from './zod-type-faker' 3 | 4 | export class ZodNeverFaker extends ZodTypeFaker { 5 | fake(): z.infer { 6 | throw Error() 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/v3/zod-void-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { ZodTypeFaker } from './zod-type-faker' 3 | 4 | export class ZodVoidFaker extends ZodTypeFaker { 5 | fake(): z.infer { 6 | return undefined 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/v3/zod-symbol-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { ZodTypeFaker } from './zod-type-faker' 3 | 4 | export class ZodSymbolFaker extends ZodTypeFaker { 5 | fake(): z.infer { 6 | return Symbol() 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/v3/zod-undefined-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { ZodTypeFaker } from './zod-type-faker' 3 | 4 | export class ZodUndefinedFaker extends ZodTypeFaker { 5 | fake(): z.infer { 6 | return void 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/v3/zod-literal-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { ZodTypeFaker } from './zod-type-faker' 3 | 4 | export class ZodLiteralFaker> extends ZodTypeFaker { 5 | fake(): z.infer { 6 | return this.schema._def.value 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", 3 | "include-component-in-tag": false, 4 | "include-v-in-tag": false, 5 | "packages": { 6 | ".": {} 7 | }, 8 | "release-type": "node" 9 | } 10 | -------------------------------------------------------------------------------- /src/v3/index.ts: -------------------------------------------------------------------------------- 1 | export { ZodSchemaFakerError } from './error' 2 | export { fake } from './fake' 3 | export { install, installCustom } from './installation' 4 | export { getFaker, installFaker, randexp, runFake, seed, setFaker } from './random' 5 | export { ZodTypeFaker } from './zod-type-faker' 6 | -------------------------------------------------------------------------------- /src/v3/zod-unknown-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { ZodTypeFaker } from './zod-type-faker' 4 | 5 | export class ZodUnknownFaker extends ZodTypeFaker { 6 | fake(): z.infer { 7 | return fake(z.any()) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/v3/zod-lazy-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { ZodTypeFaker } from './zod-type-faker' 4 | 5 | export class ZodLazyFaker extends ZodTypeFaker { 6 | fake(): z.infer { 7 | return fake(this.schema._def.getter()) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | printWidth: 120, 3 | tabWidth: 2, 4 | useTabs: false, 5 | semi: false, 6 | singleQuote: true, 7 | quoteProps: 'as-needed', 8 | trailingComma: 'all', 9 | bracketSpacing: true, 10 | arrowParens: 'avoid', 11 | proseWrap: 'always', 12 | endOfLine: 'lf', 13 | } 14 | -------------------------------------------------------------------------------- /src/v3/zod-boolean-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { getFaker } from './random' 3 | import { ZodTypeFaker } from './zod-type-faker' 4 | 5 | export class ZodBooleanFaker extends ZodTypeFaker { 6 | fake(): z.infer { 7 | return getFaker().datatype.boolean() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/v3/zod-branded-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { ZodTypeFaker } from './zod-type-faker' 4 | 5 | export class ZodBrandedFaker> extends ZodTypeFaker { 6 | fake(): z.infer { 7 | return fake(this.schema._def.type) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/v3/zod-pipe-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { ZodTypeFaker } from './zod-type-faker' 4 | 5 | export class ZodPipelineFaker> extends ZodTypeFaker { 6 | fake(): z.infer { 7 | return fake(this.schema._def.out) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/v3/zod-enum-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { getFaker } from './random' 3 | import { ZodTypeFaker } from './zod-type-faker' 4 | 5 | export class ZodEnumFaker> extends ZodTypeFaker { 6 | fake(): z.infer { 7 | return getFaker().helpers.arrayElement(this.schema._def.values) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/nan.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeNaN(schema: T, context: Context, rootFake: typeof internalFake): Infer { 7 | return NaN 8 | } 9 | -------------------------------------------------------------------------------- /src/v3/zod-readonly-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { ZodTypeFaker } from './zod-type-faker' 4 | 5 | export class ZodReadonlyFaker> extends ZodTypeFaker { 6 | fake(): z.infer { 7 | return Object.freeze(fake(this.schema._def.innerType)) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/any.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeAny(schema: T, context: Context, rootFake: typeof internalFake): Infer { 7 | return undefined 8 | } 9 | -------------------------------------------------------------------------------- /src/v3/zod-native-enum-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { getFaker } from './random' 3 | import { ZodTypeFaker } from './zod-type-faker' 4 | 5 | export class ZodNativeEnumFaker> extends ZodTypeFaker { 6 | fake(): z.infer { 7 | return getFaker().helpers.enumValue(this.schema._def.values) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /e2e/issue-189/v3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "issue-189", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "test": "npx tsc -p tsconfig.json" 7 | }, 8 | "dependencies": { 9 | "zod": "3.25.76" 10 | }, 11 | "devDependencies": { 12 | "@types/node": "24.10.4", 13 | "ts-expect": "1.3.0", 14 | "typescript": "5.9.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/v3/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /examples/v4/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/null.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeNull( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | return null 12 | } 13 | -------------------------------------------------------------------------------- /src/v4/internals/type.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from './context' 3 | 4 | export type Infer = T['_zod']['output'] 5 | 6 | export type Fake = (schema: T, context: Context, fake: RootFake) => core.infer 7 | 8 | export type RootFake = (schema: T, context: Context) => core.infer 9 | -------------------------------------------------------------------------------- /src/v3/zod-union-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { getFaker } from './random' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | export class ZodUnionFaker> extends ZodTypeFaker { 7 | fake(): z.infer { 8 | return fake(getFaker().helpers.arrayElement(this.schema._def.options)) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/never.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeNever( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | throw Error() 12 | } 13 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/void.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeVoid( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | return undefined 12 | } 13 | -------------------------------------------------------------------------------- /src/v3/zod-function-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { ZodTypeFaker } from './zod-type-faker' 4 | 5 | export class ZodFunctionFaker> extends ZodTypeFaker { 6 | fake(): z.infer { 7 | return (..._: Parameters>) => { 8 | return fake(this.schema._def.returns) 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/symbol.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeSymbol( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | return Symbol() 12 | } 13 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/unknown.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeUnknown( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | return undefined 12 | } 13 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | 6 | permissions: 7 | contents: write 8 | pull-requests: write 9 | 10 | name: release-please 11 | 12 | jobs: 13 | release-please: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: googleapis/release-please-action@v4.4.0 17 | with: 18 | token: ${{ secrets.RELEASE_PLEASE }} 19 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/undefined.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeUndefined( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | return undefined 12 | } 13 | -------------------------------------------------------------------------------- /src/v4/internals/utils.ts: -------------------------------------------------------------------------------- 1 | export function unescape(RegExp: string): string { 2 | return RegExp.replace(/\\([.*+?^${}()|[\]\\])/g, '$1') 3 | } 4 | 5 | export function lcm(a: T, b: T): T { 6 | return ((a * b) / gcd(a, b)) as T 7 | } 8 | 9 | export function gcd(a: T, b: T): T { 10 | return b === 0n || b === 0 ? a : gcd(b, (a % b) as T) 11 | } 12 | -------------------------------------------------------------------------------- /src/v3/zod-nullable-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { getFaker } from './random' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | export class ZodNullableFaker> extends ZodTypeFaker { 7 | fake(): z.infer { 8 | return getFaker().datatype.boolean() ? null : fake(this.schema._def.innerType) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /e2e/issue-189/v4/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "issue-189", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "test": "npx tsc -p tsconfig.json" 7 | }, 8 | "dependencies": { 9 | "zod": "4.1.9" 10 | }, 11 | "devDependencies": { 12 | "@faker-js/faker": "10.1.0", 13 | "@types/node": "24.10.4", 14 | "ts-expect": "1.3.0", 15 | "typescript": "5.9.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | docs 27 | coverage 28 | 29 | *.tgz 30 | -------------------------------------------------------------------------------- /examples/v3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "tsc && vite build", 7 | "dev": "vite", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "zod": "3.25.76" 12 | }, 13 | "devDependencies": { 14 | "@types/node": "22.14.1", 15 | "typescript": "5.8.3", 16 | "vite": "6.3.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/lazy.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeLazy( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | return rootFake(schema._zod.def.getter(), context) 12 | } 13 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/catch.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeCatch( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | return rootFake(schema._zod.def.innerType, context) 12 | } 13 | -------------------------------------------------------------------------------- /examples/v3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/v4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSpacing": true, 4 | "endOfLine": "lf", 5 | "plugins": ["prettier-plugin-sort-json", "prettier-plugin-packagejson", "prettier-plugin-organize-imports"], 6 | "printWidth": 120, 7 | "proseWrap": "always", 8 | "quoteProps": "as-needed", 9 | "semi": false, 10 | "singleQuote": true, 11 | "tabWidth": 2, 12 | "trailingComma": "all", 13 | "useTabs": false 14 | } 15 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/non-optional.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeNonOptional( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | return rootFake(schema._zod.def.innerType, context) 12 | } 13 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/promise.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakePromise( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | return Promise.resolve(rootFake(schema._zod.def.innerType, context)) 12 | } 13 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/readonly.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeReadonly( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | return Object.freeze(rootFake(schema._zod.def.innerType, context)) 12 | } 13 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: auto 6 | threshold: 0% 7 | base: auto 8 | if_ci_failed: error 9 | informational: false 10 | only_pulls: false 11 | 12 | patch: 13 | default: 14 | target: auto 15 | threshold: 0% 16 | base: auto 17 | if_ci_failed: error 18 | informational: false 19 | only_pulls: false 20 | -------------------------------------------------------------------------------- /examples/v4/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "tsc && vite build", 7 | "dev": "vite", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "zod": "4.1.9" 12 | }, 13 | "devDependencies": { 14 | "@faker-js/faker": "10.1.0", 15 | "@types/node": "22.14.1", 16 | "typescript": "5.8.3", 17 | "vite": "6.3.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/boolean.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakeBoolean( 8 | schema: T, 9 | context: Context, 10 | rootFake: typeof internalFake, 11 | ): Infer { 12 | return getFaker().datatype.boolean() 13 | } 14 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "automerge": true, 4 | "automergeStrategy": "squash", 5 | "automergeType": "pr", 6 | "extends": ["config:recommended"], 7 | "packageRules": [ 8 | { 9 | "matchPackageNames": ["*"], 10 | "rangeStrategy": "pin" 11 | }, 12 | { 13 | "matchDepTypes": ["engines", "peerDependencies"], 14 | "rangeStrategy": "auto" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /src/v3/zod-tuple-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { ZodTypeFaker } from './zod-type-faker' 4 | 5 | export class ZodTupleFaker> extends ZodTypeFaker { 6 | fake(): z.infer { 7 | return [ 8 | ...this.schema._def.items.map((item: any) => fake(item)), 9 | ...(this.schema._def.rest ? [fake(this.schema._def.rest)] : []), 10 | ] satisfies z.infer 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/v3/zod-optional-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { getFaker } from './random' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | export class ZodOptionalFaker> extends ZodTypeFaker { 7 | fake(): z.infer { 8 | if (getFaker().datatype.boolean()) { 9 | return undefined 10 | } else { 11 | return fake(this.schema._def.innerType) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/v3/zod-branded.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest' 2 | import { z } from 'zod/v3' 3 | import { fake, install } from '../../src/v3' 4 | 5 | test('branded', () => { 6 | install() 7 | 8 | const Cat = z.object({ name: z.string() }).brand<'Cat'>() 9 | type Cat = z.infer 10 | 11 | const data = fake(Cat) 12 | 13 | expect(Cat.safeParse(data).success).toBe(true) 14 | 15 | const petCat = (cat: Cat) => {} 16 | petCat(data) 17 | }) 18 | -------------------------------------------------------------------------------- /src/v3/zod-discriminated-union-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { getFaker } from './random' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | export class ZodDiscriminatedUnionFaker> extends ZodTypeFaker { 7 | fake(): z.infer { 8 | const randomSchema = getFaker().helpers.arrayElement(this.schema.options) 9 | return fake(randomSchema as any) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/enum.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakeEnum( 8 | schema: T, 9 | context: Context, 10 | rootFake: typeof internalFake, 11 | ): Infer { 12 | return getFaker().helpers.objectValue(schema._zod.def.entries) 13 | } 14 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/literal.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakeLiteral( 8 | schema: T, 9 | context: Context, 10 | rootFake: typeof internalFake, 11 | ): Infer { 12 | return getFaker().helpers.arrayElement(schema._zod.def.values) 13 | } 14 | -------------------------------------------------------------------------------- /src/v3/zod-default-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { getFaker } from './random' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | export class ZodDefaultFaker> extends ZodTypeFaker { 7 | fake(): z.infer { 8 | if (getFaker().datatype.boolean()) { 9 | return this.schema._def.defaultValue() 10 | } else { 11 | return fake(this.schema._def.innerType) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /e2e/issue-189/v3/src/index.ts: -------------------------------------------------------------------------------- 1 | import { expectType, type TypeEqual } from 'ts-expect' 2 | import { z } from 'zod' 3 | import { fake, install } from 'zod-schema-faker' 4 | 5 | const schema = z.number() 6 | 7 | // enable tree shaking 8 | if (process.env.NODE_ENV !== 'production') { 9 | install() 10 | 11 | const data = fake(schema) 12 | 13 | console.log(data) // => -2556.9 14 | 15 | expectType>(false) 16 | expectType>(true) 17 | } 18 | -------------------------------------------------------------------------------- /src/v3/zod-map-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { getFaker } from './random' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | export class ZodMapFaker> extends ZodTypeFaker { 7 | fake(): z.infer { 8 | return new Map( 9 | getFaker().helpers.multiple(() => [fake(this.schema._def.keyType), fake(this.schema._def.valueType)], { 10 | count: { min: 1, max: 10 }, 11 | }), 12 | ) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/v3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ESNext", "DOM"], 7 | "moduleResolution": "Node", 8 | "strict": true, 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "esModuleInterop": true, 12 | "noEmit": true, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "noImplicitReturns": true 16 | }, 17 | "include": ["src"] 18 | } 19 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/nullable.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakeNullable( 8 | schema: T, 9 | context: Context, 10 | rootFake: typeof internalFake, 11 | ): Infer { 12 | return getFaker().datatype.boolean() ? rootFake(schema._zod.def.innerType, context) : null 13 | } 14 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/optional.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakeOptional( 8 | schema: T, 9 | context: Context, 10 | rootFake: typeof internalFake, 11 | ): Infer { 12 | return getFaker().datatype.boolean() ? rootFake(schema._zod.def.innerType, context) : undefined 13 | } 14 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/default.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakeDefault( 8 | schema: T, 9 | context: Context, 10 | rootFake: typeof internalFake, 11 | ): Infer { 12 | return getFaker().datatype.boolean() ? rootFake(schema._zod.def.innerType, context) : schema._zod.def.defaultValue 13 | } 14 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/prefault.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakePrefault( 8 | schema: T, 9 | context: Context, 10 | rootFake: typeof internalFake, 11 | ): Infer { 12 | return getFaker().datatype.boolean() ? rootFake(schema._zod.def.innerType, context) : schema._zod.def.defaultValue 13 | } 14 | -------------------------------------------------------------------------------- /tests/v3/zod-pipe-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { fake, install } from '../../src/v3' 5 | 6 | test('pipe', () => { 7 | const _in = z.string().transform(val => val.length) 8 | const out = z.number().min(5).int() 9 | const schema = _in.pipe(out) 10 | 11 | install() 12 | const data = fake(schema) 13 | 14 | expectType>(true) 15 | expect(out.safeParse(data).success).toBe(true) 16 | }) 17 | -------------------------------------------------------------------------------- /e2e/issue-189/v4/src/index.ts: -------------------------------------------------------------------------------- 1 | import { faker } from '@faker-js/faker' 2 | import { expectType, type TypeEqual } from 'ts-expect' 3 | import { z } from 'zod' 4 | import { fake, setFaker } from 'zod-schema-faker/v4' 5 | 6 | const schema = z.number() 7 | 8 | // enable tree shaking 9 | if (process.env.NODE_ENV !== 'production') { 10 | setFaker(faker) 11 | 12 | const data = fake(schema) 13 | 14 | console.log(data) // => -2556.9 15 | 16 | expectType>(false) 17 | expectType>(true) 18 | } 19 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/custom.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { Fake, Infer, RootFake } from '../type' 4 | 5 | const customs = new Map>() 6 | export function custom(schema: T, fake: Fake): void { 7 | customs.set(schema, fake as any) 8 | } 9 | 10 | export function fakeCustom(schema: T, context: Context, rootFake: RootFake): Infer { 11 | return customs.get(schema)!(schema, context, rootFake) 12 | } 13 | -------------------------------------------------------------------------------- /src/v3/zod-type-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { assertsZodSchema } from './utils' 3 | 4 | /** 5 | * Base class for fakers. 6 | */ 7 | export abstract class ZodTypeFaker { 8 | constructor(protected schema: T) { 9 | assertsZodSchema(schema) 10 | } 11 | 12 | abstract fake(): z.infer 13 | } 14 | 15 | export class ZodTypeFakerConcrete extends ZodTypeFaker { 16 | /* v8 ignore next 3 */ 17 | fake(): z.infer { 18 | throw new Error('Method not implemented.') 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/template-literal.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeTemplateLiteral( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | return schema._zod.def.parts 12 | .map(part => (part instanceof core.$ZodType ? rootFake(part, context) : part)) 13 | .map(part => part + '') 14 | .join('') 15 | } 16 | -------------------------------------------------------------------------------- /src/v3/zod-catch-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { getFaker } from './random' 4 | import { ZodNeverFaker } from './zod-never-faker' 5 | import { ZodTypeFaker } from './zod-type-faker' 6 | 7 | export class ZodCatchFaker> extends ZodTypeFaker { 8 | fake(): z.infer> { 9 | if (getFaker().datatype.boolean()) { 10 | return this.schema.parse(new ZodNeverFaker(z.never())) 11 | } else { 12 | return fake(this.schema._def.innerType) 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/v3/zod-promise-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { getFaker } from './random' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | export class ZodPromiseFaker> extends ZodTypeFaker { 7 | fake(): z.infer { 8 | const data = fake(this.schema._def.type) 9 | if (getFaker().datatype.boolean()) { 10 | return Promise.resolve(data) 11 | } else { 12 | return new Promise(resolve => { 13 | setTimeout(() => resolve(data)) 14 | }) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/function.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { Infer } from '../type' 5 | 6 | export function fakeFunction( 7 | schema: T, 8 | context: Context, 9 | rootFake: typeof internalFake, 10 | ): Infer { 11 | return (...args: Infer): Infer => { 12 | core.parse(schema._zod.def.input, args) 13 | return rootFake(schema._zod.def.output, context) as Infer 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/map.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakeMap(schema: T, context: Context, rootFake: typeof internalFake): Infer { 8 | return new Map( 9 | getFaker().helpers.multiple( 10 | () => [rootFake(schema._zod.def.keyType, context), rootFake(schema._zod.def.valueType, context)], 11 | { 12 | count: { min: 0, max: 10 }, 13 | }, 14 | ), 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/date.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakeDate( 8 | schema: T, 9 | context: Context, 10 | rootFake: typeof internalFake, 11 | ): Infer { 12 | const minDate: Date = schema._zod.bag.minimum ?? new Date(-8640000000000000) 13 | const maxDate: Date = schema._zod.bag.maximum ?? new Date(8640000000000000) 14 | return getFaker().date.between({ from: minDate, to: maxDate }) 15 | } 16 | -------------------------------------------------------------------------------- /examples/v4/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true 21 | }, 22 | "include": ["src"] 23 | } 24 | -------------------------------------------------------------------------------- /examples/v3/src/main.ts: -------------------------------------------------------------------------------- 1 | import { fake, install } from 'zod-schema-faker' 2 | import { z } from 'zod/v3' 3 | 4 | const main = async () => { 5 | const schema = z.string().uuid() 6 | let data: string 7 | 8 | if (process.env.NODE_ENV !== 'production') { 9 | install() 10 | 11 | data = fake(schema) 12 | } else { 13 | data = await fetch('https://httpbin.org/uuid') 14 | .then(response => response.json()) 15 | .then(json => json.uuid) 16 | } 17 | 18 | const app = document.querySelector('#app')! 19 | app.innerHTML = `
${JSON.stringify(schema.safeParse(data), null, 2)}
` 20 | } 21 | 22 | main() 23 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/union.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakeUnion( 8 | schema: T, 9 | context: Context, 10 | rootFake: typeof internalFake, 11 | ): Infer { 12 | const options = schema._zod.def.options.filter(option => option._zod.def.type !== 'never') 13 | if (options.length === 0) { 14 | options.push(schema._zod.def.options[0]) 15 | } 16 | return rootFake(getFaker().helpers.arrayElement(options), context) 17 | } 18 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/tuple.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakeTuple( 8 | schema: T, 9 | context: Context, 10 | rootFake: typeof internalFake, 11 | ): Infer { 12 | return schema._zod.def.items 13 | .map(item => rootFake(item, context)) 14 | .concat( 15 | schema._zod.def.rest 16 | ? getFaker().helpers.multiple(() => rootFake(schema._zod.def.rest!, context), { count: { min: 0, max: 5 } }) 17 | : [], 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /examples/v4/src/main.ts: -------------------------------------------------------------------------------- 1 | import { faker } from '@faker-js/faker' 2 | import { fake, setFaker } from 'zod-schema-faker/v4' 3 | import { z } from 'zod/mini' 4 | 5 | const main = async () => { 6 | const schema = z.uuid() 7 | let data: string 8 | 9 | if (process.env.NODE_ENV !== 'production') { 10 | setFaker(faker) 11 | 12 | data = fake(schema) 13 | } else { 14 | data = await fetch('https://httpbin.org/uuid') 15 | .then(response => response.json()) 16 | .then(json => json.uuid) 17 | } 18 | 19 | const app = document.querySelector('#app')! 20 | app.innerHTML = `
${JSON.stringify(schema.safeParse(data), null, 2)}
` 21 | } 22 | 23 | main() 24 | -------------------------------------------------------------------------------- /src/v3/utils.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { ZodSchemaFakerError } from './error' 3 | 4 | export function assertsZodSchema(schema: unknown): asserts schema is z.ZodTypeAny { 5 | if ( 6 | typeof schema !== 'object' || 7 | schema === null || 8 | '_parse' in schema === false || 9 | typeof schema._parse !== 'function' 10 | ) { 11 | throw new ZodSchemaFakerError(`Expected a zod schema, but got ${schema}`) 12 | } 13 | } 14 | 15 | export function lcm(a: T, b: T): T { 16 | return ((a * b) / gcd(a, b)) as T 17 | } 18 | 19 | export function gcd(a: T, b: T): T { 20 | return b === 0n || b === 0 ? a : gcd(b, (a % b) as T) 21 | } 22 | -------------------------------------------------------------------------------- /tests/v3/fake.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from 'vitest' 2 | import { z } from 'zod/v3' 3 | import { fake, install } from '../../src/v3' 4 | import { uninstall } from '../../src/v3/installation' 5 | 6 | describe('fake', () => { 7 | test('fake is a function', () => { 8 | expect(typeof fake).toBe('function') 9 | }) 10 | 11 | test('fake should assert parameters', () => { 12 | expect(() => fake(undefined as any)).toThrow() 13 | }) 14 | 15 | test('fake should accepts a ZodType schema', () => { 16 | install() 17 | expect(() => fake(z.number())).not.toThrow() 18 | uninstall() 19 | }) 20 | 21 | test('fake should throw an error if not installed', () => { 22 | expect(() => fake(z.number())).toThrow() 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /tests/v3/util.ts: -------------------------------------------------------------------------------- 1 | import { test } from 'vitest' 2 | 3 | const count = 50 4 | 5 | interface TestMultipleTimes { 6 | (name: string, fn: () => void): void 7 | only(name: string, fn: () => void): void 8 | skip(name: string, fn: () => void): void 9 | } 10 | 11 | export const testMultipleTimes: TestMultipleTimes = (name, fn) => { 12 | test(name, () => { 13 | for (let i = 0; i < count; i++) { 14 | fn() 15 | } 16 | }) 17 | } 18 | 19 | testMultipleTimes.only = (name, fn) => { 20 | test.only(name, () => { 21 | for (let i = 0; i < count; i++) { 22 | fn() 23 | } 24 | }) 25 | } 26 | 27 | testMultipleTimes.skip = (name, fn) => { 28 | test.skip(name, () => { 29 | for (let i = 0; i < count; i++) { 30 | fn() 31 | } 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /src/v3/zod-any-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { getFaker } from './random' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | const schemas = (() => { 7 | const simpleSchemas = [z.string(), z.number(), z.boolean(), z.null()] 8 | const arraySchemas = simpleSchemas.map(schema => z.array(schema)) 9 | const objectSchemas = simpleSchemas.map(schema => 10 | z.object({ 11 | foo: schema, 12 | }), 13 | ) 14 | 15 | return [...simpleSchemas, ...arraySchemas, ...objectSchemas] 16 | })() 17 | 18 | export class ZodAnyFaker extends ZodTypeFaker { 19 | fake(): z.infer { 20 | const randomSchema = getFaker().helpers.arrayElement(schemas) 21 | return fake(randomSchema) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/v3/zod-record-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { ZodSchemaFakerError } from './error' 3 | import { fake } from './fake' 4 | import { getFaker } from './random' 5 | import { ZodTypeFaker } from './zod-type-faker' 6 | 7 | export class ZodRecordFaker> extends ZodTypeFaker { 8 | fake(): z.infer { 9 | if (this.schema._def.keyType instanceof z.ZodString === false) { 10 | throw new ZodSchemaFakerError('Invalid record key type: In runtime JavaScript, all keys are strings.') 11 | } 12 | 13 | return Object.fromEntries( 14 | getFaker().helpers.multiple(() => [fake(this.schema._def.keyType), fake(this.schema._def.valueType)], { 15 | count: { min: 0, max: 10 }, 16 | }), 17 | ) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/v3/zod-set-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { getFaker } from './random' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | export class ZodSetFaker> extends ZodTypeFaker { 7 | fake(): z.infer { 8 | const min = this.schema._def.minSize?.value ?? 0 9 | const max = this.schema._def.maxSize?.value ?? getFaker().number.int({ min, max: min + 10 }) 10 | 11 | if (min > max) { 12 | throw new RangeError() 13 | } 14 | 15 | const set = new Set() 16 | while (set.size < min) { 17 | set.add(fake(this.schema._def.valueType)) 18 | } 19 | while (set.size < max && getFaker().datatype.boolean()) { 20 | set.add(fake(this.schema._def.valueType)) 21 | } 22 | return set 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/v3/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest' 2 | import { z } from 'zod/v3' 3 | import { assertsZodSchema } from '../../src/v3/utils' 4 | 5 | test('assertsZodSchema should throw an error if schema is not a zod schema', () => { 6 | expect(() => assertsZodSchema(null)).toThrow() 7 | expect(() => assertsZodSchema(undefined)).toThrow() 8 | expect(() => assertsZodSchema(42)).toThrow() 9 | expect(() => assertsZodSchema('foo')).toThrow() 10 | expect(() => assertsZodSchema({})).toThrow() 11 | }) 12 | 13 | test('assertsZodSchema should not throw an error if schema is a zod schema', () => { 14 | expect(() => assertsZodSchema(z.null())).not.toThrow() 15 | expect(() => assertsZodSchema(z.undefined())).not.toThrow() 16 | expect(() => assertsZodSchema(z.number())).not.toThrow() 17 | expect(() => assertsZodSchema(z.string())).not.toThrow() 18 | expect(() => assertsZodSchema(z.object({}))).not.toThrow() 19 | }) 20 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/object.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakeObject( 8 | schema: T, 9 | context: Context, 10 | rootFake: typeof internalFake, 11 | ): Infer { 12 | return Object.fromEntries( 13 | Object.entries(schema._zod.def.shape) 14 | .concat( 15 | schema._zod.def.catchall && schema._zod.def.catchall._zod.def.type !== 'never' 16 | ? getFaker() 17 | .helpers.multiple(() => getFaker().string.uuid()) 18 | .filter(key => !schema._zod.def.shape[key]) 19 | .map(key => [key, schema._zod.def.catchall!]) 20 | : [], 21 | ) 22 | .map(([key, value]) => [key, rootFake(value, { ...context, depth: context.depth + 1 })]), 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "allowSyntheticDefaultImports": true, 11 | "moduleResolution": "node", 12 | "isolatedModules": true, 13 | "isolatedDeclarations": true, 14 | "noEmit": true, 15 | "declaration": true, 16 | "esModuleInterop": true, 17 | 18 | /* Linting */ 19 | "strict": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "strictBindCallApply": true, 22 | "strictNullChecks": true, 23 | "strictFunctionTypes": true, 24 | "strictPropertyInitialization": true, 25 | "stripInternal": true, 26 | "noImplicitAny": true, 27 | "noImplicitOverride": true, 28 | "noImplicitReturns": true, 29 | "noImplicitThis": true 30 | }, 31 | "include": ["src", "tests"] 32 | } 33 | -------------------------------------------------------------------------------- /src/v3/zod-effects-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { ZodTypeFaker } from './zod-type-faker' 4 | 5 | export class ZodEffectsFaker> extends ZodTypeFaker { 6 | fake(): z.infer { 7 | let result: any 8 | switch (this.schema._def.effect.type) { 9 | case 'preprocess': 10 | result = fake(this.schema._def.schema) 11 | break 12 | 13 | case 'refinement': 14 | result = fake(this.schema._def.schema) 15 | break 16 | 17 | case 'transform': 18 | result = this.schema._def.effect.transform(fake(this.schema._def.schema), { 19 | /* v8 ignore next 1 */ 20 | addIssue: () => void 0, 21 | path: [], 22 | }) 23 | break 24 | 25 | /* v8 ignore next 3 */ 26 | default: { 27 | const _: never = this.schema._def.effect 28 | } 29 | } 30 | 31 | return result 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/pipe.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | const truthyValues = ['true', '1', 'yes', 'on', 'y', 'enabled'] 8 | const falsyValues = ['false', '0', 'no', 'off', 'n', 'disabled'] 9 | const stringBoolValues = [...truthyValues, ...falsyValues] 10 | 11 | export function fakePipe( 12 | schema: T, 13 | context: Context, 14 | rootFake: typeof internalFake, 15 | ): Infer { 16 | if (schema._zod.def.out._zod.def.type === 'boolean') { 17 | return getFaker().helpers.arrayElement(stringBoolValues) 18 | } 19 | 20 | const left = rootFake(schema._zod.def.in, context) as any 21 | const payload = { value: left, issues: left.issues } 22 | const _context = {} 23 | const right = schema._zod.def.out._zod.run(payload, _context) 24 | payload.value = right 25 | return left 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ernest 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/record.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | 7 | export function fakeRecord( 8 | schema: T, 9 | context: Context, 10 | rootFake: typeof internalFake, 11 | ): Infer { 12 | if (schema._zod.def.keyType instanceof core.$ZodEnum) { 13 | return Object.fromEntries( 14 | Object.values(schema._zod.def.keyType._zod.def.entries).map(key => [ 15 | key, 16 | rootFake(schema._zod.def.valueType, context), 17 | ]), 18 | ) 19 | } else if (schema._zod.def.keyType instanceof core.$ZodLiteral) { 20 | return Object.fromEntries( 21 | schema._zod.def.keyType._zod.def.values.map(key => [key, rootFake(schema._zod.def.valueType, context)]), 22 | ) 23 | } else { 24 | return Object.fromEntries( 25 | getFaker().helpers.multiple(() => [ 26 | rootFake(schema._zod.def.keyType, context), 27 | rootFake(schema._zod.def.valueType, context), 28 | ]), 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/v4/internals/random.ts: -------------------------------------------------------------------------------- 1 | import { Faker, SimpleFaker } from '@faker-js/faker' 2 | import RandExp from 'randexp' 3 | 4 | let _faker: Faker 5 | 6 | /** 7 | * Set the faker instance to use. 8 | */ 9 | export function setFaker(faker: Faker): void { 10 | _faker = faker 11 | } 12 | 13 | /** 14 | * Get the faker instance. 15 | */ 16 | export function getFaker(): Faker { 17 | if (_faker) { 18 | return _faker 19 | } else { 20 | throw ReferenceError('Faker instance not set. Use `setFaker` to set it.') 21 | } 22 | } 23 | 24 | const _simpleFaker = new SimpleFaker() 25 | /** 26 | * Create random strings that match a given regular expression. 27 | */ 28 | export const randexp = (pattern: string | RegExp, flags?: string): string => { 29 | const randexp = new RandExp(pattern, flags) 30 | randexp.randInt = (from, to) => _simpleFaker.number.int({ min: from, max: to }) 31 | return randexp.gen() 32 | } 33 | 34 | /** 35 | * Sets the seed or generates a new one. 36 | * 37 | * This method is intended to allow for consistent values in tests, so you might want to use hardcoded values as the seed. 38 | */ 39 | export const seed = (value?: number): void => { 40 | _faker.seed(value) 41 | _simpleFaker.seed(value) 42 | } 43 | -------------------------------------------------------------------------------- /src/v3/zod-array-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { getFaker } from './random' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | export class ZodArrayFaker> extends ZodTypeFaker { 7 | fake(): z.infer { 8 | if (this.schema._def.exactLength !== null) { 9 | if ( 10 | this.schema._def.minLength !== null && 11 | this.schema._def.minLength.value !== this.schema._def.exactLength.value 12 | ) { 13 | throw new RangeError() 14 | } 15 | if ( 16 | this.schema._def.maxLength !== null && 17 | this.schema._def.maxLength.value !== this.schema._def.exactLength.value 18 | ) { 19 | throw new RangeError() 20 | } 21 | } 22 | const min = this.schema._def.exactLength?.value ?? this.schema._def.minLength?.value ?? 0 23 | const max = 24 | this.schema._def.exactLength?.value ?? 25 | this.schema._def.maxLength?.value ?? 26 | getFaker().number.int({ min, max: min + 2 }) 27 | if (min > max) { 28 | throw new RangeError() 29 | } 30 | return getFaker().helpers.multiple(() => fake(this.schema._def.type), { count: { min, max } }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/v3/fake.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { ZodSchemaFakerError } from './error' 3 | import { ZodTypeFakerConcrete } from './zod-type-faker' 4 | 5 | export const zodFirstPartyTypeKindToZodTypeFaker: Map< 6 | z.ZodFirstPartyTypeKind, 7 | typeof ZodTypeFakerConcrete 8 | > = new Map() 9 | export const zodTypeToZodTypeFaker: Map> = new Map() 10 | 11 | /** 12 | * Generate fake data based on schema. 13 | * 14 | * @throws when a corresponding faker is not registered. 15 | */ 16 | export const fake = (schema: T): z.infer => { 17 | const typeName = (schema._def as any).typeName 18 | const Faker = zodTypeToZodTypeFaker.get(schema) ?? zodFirstPartyTypeKindToZodTypeFaker.get(typeName) 19 | if (Faker === undefined) { 20 | throw new ZodSchemaFakerError(`Unsupported schema type: ${typeName}. 21 | - If this is a custom schema, you may not have installed a corresponding faker. If you need help with installation, please refer to the documentation for more information. 22 | - If this is a built-in schema, it may not be implemented yet. Please file an issue to let us know: https://github.com/soc221b/zod-schema-faker/issues`) 23 | } 24 | 25 | return new Faker(schema).fake() 26 | } 27 | -------------------------------------------------------------------------------- /tests/v3/zod-never-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { ZodNeverFaker } from '../../src/v3/zod-never-faker' 5 | 6 | test('ZodNeverFaker should assert parameters', () => { 7 | const invalidSchema = void 0 as any 8 | expect(() => new ZodNeverFaker(invalidSchema)).toThrow() 9 | }) 10 | 11 | test('ZodNeverFaker should accepts a ZodNever schema', () => { 12 | const schema = z.never() 13 | expect(() => new ZodNeverFaker(schema)).not.toThrow() 14 | }) 15 | 16 | test('ZodNeverFaker should return a ZodNeverFaker instance', () => { 17 | const schema = z.never() 18 | const faker = new ZodNeverFaker(schema) 19 | expect(faker instanceof ZodNeverFaker).toBe(true) 20 | }) 21 | 22 | test('ZodNeverFaker.fake should be a function', () => { 23 | const schema = z.never() 24 | const faker = new ZodNeverFaker(schema) 25 | expect(typeof faker.fake).toBe('function') 26 | }) 27 | 28 | test('ZodNeverFaker.fake should return never type', () => { 29 | const schema = z.never() 30 | const faker = new ZodNeverFaker(schema) 31 | expectType, never>>(true) 32 | }) 33 | 34 | test('ZodNullFaker.fake should throw an error', () => { 35 | const schema = z.never() 36 | const faker = new ZodNeverFaker(schema) 37 | expect(() => faker.fake()).toThrow() 38 | }) 39 | -------------------------------------------------------------------------------- /tests/v3/zod-nan-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { ZodNaNFaker } from '../../src/v3/zod-nan-faker' 5 | 6 | test('ZodNaNFaker should assert parameters', () => { 7 | const invalidSchema = void 0 as any 8 | expect(() => new ZodNaNFaker(invalidSchema)).toThrow() 9 | }) 10 | 11 | test('ZodNaNFaker should accepts a ZodNaN schema', () => { 12 | const schema = z.nan() 13 | expect(() => new ZodNaNFaker(schema)).not.toThrow() 14 | }) 15 | 16 | test('ZodNaNFaker should return a ZodNaNFaker instance', () => { 17 | const schema = z.nan() 18 | const faker = new ZodNaNFaker(schema) 19 | expect(faker instanceof ZodNaNFaker).toBe(true) 20 | }) 21 | 22 | test('ZodNaNFaker.fake should be a function', () => { 23 | const schema = z.nan() 24 | const faker = new ZodNaNFaker(schema) 25 | expect(typeof faker.fake).toBe('function') 26 | }) 27 | 28 | test('ZodNaNFaker.fake should return number type', () => { 29 | const schema = z.nan() 30 | const faker = new ZodNaNFaker(schema) 31 | expectType, number>>(true) 32 | }) 33 | 34 | test('ZodNaNFaker.fake should return a valid data', () => { 35 | const schema = z.nan() 36 | const faker = new ZodNaNFaker(schema) 37 | const data = faker.fake() 38 | expect(schema.safeParse(data).success).toBe(true) 39 | }) 40 | -------------------------------------------------------------------------------- /tests/v3/zod-instanceof-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { beforeEach, expect, test } from 'vitest' 2 | import { z } from 'zod/v3' 3 | import { fake, getFaker, install, installCustom, ZodTypeFaker } from '../../src/v3' 4 | 5 | interface UserLike { 6 | name: string 7 | } 8 | 9 | class User implements UserLike { 10 | constructor(public name: string) {} 11 | } 12 | 13 | // 1/5. define custom schema 14 | const UserSchema = z.instanceof(User).and(z.object({ name: z.string() }).strict()) 15 | 16 | // 2/5. define custom faker 17 | class UserFaker extends ZodTypeFaker { 18 | fake(): User { 19 | return new User(`${getFaker().person.firstName()}`) 20 | } 21 | } 22 | 23 | beforeEach(() => { 24 | // 3/5. install basic faker 25 | install() 26 | // 4/5. install custom faker 27 | installCustom(UserSchema, UserFaker) 28 | }) 29 | 30 | test('instanceof', () => { 31 | // 5/5. use it 32 | const data = fake(UserSchema) 33 | expect(UserSchema.safeParse(data).success).toBe(true) 34 | 35 | const userLike: UserLike = { name: 'foo' } 36 | const user: User = new User('bar') 37 | expect(UserSchema.safeParse(user).data).toEqual(user) 38 | expect(UserSchema.safeParse(userLike).error).toMatchInlineSnapshot(` 39 | [ZodError: [ 40 | { 41 | "code": "custom", 42 | "message": "Input not instance of User", 43 | "fatal": true, 44 | "path": [] 45 | } 46 | ]] 47 | `) 48 | }) 49 | -------------------------------------------------------------------------------- /examples/v4/src/typescript.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/v3/zod-null-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { ZodNullFaker } from '../../src/v3/zod-null-faker' 5 | 6 | test('ZodNullFaker should assert parameters', () => { 7 | const invalidSchema = void 0 as any 8 | expect(() => new ZodNullFaker(invalidSchema)).toThrow() 9 | }) 10 | 11 | test('ZodNullFaker should accepts a ZodNull schema', () => { 12 | const schema = z.null() 13 | expect(() => new ZodNullFaker(schema)).not.toThrow() 14 | }) 15 | 16 | test('ZodNullFaker should return a ZodNullFaker instance', () => { 17 | const schema = z.null() 18 | const faker = new ZodNullFaker(schema) 19 | expect(faker instanceof ZodNullFaker).toBe(true) 20 | }) 21 | 22 | test('ZodNullFaker.fake should be a function', () => { 23 | const schema = z.null() 24 | const faker = new ZodNullFaker(schema) 25 | expect(typeof faker.fake).toBe('function') 26 | }) 27 | 28 | test('ZodNullFaker.fake should return null type', () => { 29 | const schema = z.null() 30 | const faker = new ZodNullFaker(schema) 31 | expectType, null>>(true) 32 | }) 33 | 34 | test('ZodNullFaker.fake should return a valid data', () => { 35 | const schema = z.null() 36 | const faker = new ZodNullFaker(schema) 37 | const data = faker.fake() 38 | expect(schema.safeParse(data).success).toBe(true) 39 | }) 40 | -------------------------------------------------------------------------------- /tests/v3/zod-custom-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { beforeEach, expect, test } from 'vitest' 2 | import { z } from 'zod/v3' 3 | import { fake, getFaker, install, installCustom, ZodTypeFaker } from '../../src/v3' 4 | 5 | // 1/5. define custom schema 6 | const pxSchema = z.custom<`${number}px`>(val => { 7 | return typeof val === 'string' ? /^\d+px$/.test(val) : false 8 | }) 9 | 10 | // 2/5. define custom faker 11 | class ZodPxFaker extends ZodTypeFaker { 12 | fake(): `${number}px` { 13 | // you can use `getFaker` to generate fake data 14 | return `${getFaker().number.int({ min: 0 })}px` 15 | // or use `randexp` if applicable 16 | // return randexp(/[1-9]\d+?px/) as `${number}px` 17 | } 18 | } 19 | 20 | beforeEach(() => { 21 | // 3/5. install basic faker 22 | install() 23 | // 4/5. install custom faker 24 | installCustom(pxSchema, ZodPxFaker) 25 | }) 26 | 27 | test('basic', () => { 28 | // 5/5. use it 29 | const data = fake(pxSchema) 30 | 31 | expect(pxSchema.safeParse(data).success).toBe(true) 32 | }) 33 | 34 | test('integration', () => { 35 | const schema = z 36 | .object({ 37 | padding: pxSchema, 38 | }) 39 | .strict() 40 | 41 | const data = fake(schema) 42 | 43 | expect(schema.safeParse(data).success).toBe(true) 44 | }) 45 | 46 | test('type', () => { 47 | // @ts-expect-error 48 | installCustom(z.custom(), ZodTypeFaker) 49 | 50 | expect(true).toBe(true) 51 | }) 52 | -------------------------------------------------------------------------------- /tests/v3/zod-any-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodAnyFaker } from '../../src/v3/zod-any-faker' 6 | 7 | test('ZodAnyFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodAnyFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodAnyFaker should accepts a ZodAny schema', () => { 13 | const schema = z.any() 14 | expect(() => new ZodAnyFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodAnyFaker should return a ZodAnyFaker instance', () => { 18 | const schema = z.any() 19 | const faker = new ZodAnyFaker(schema) 20 | expect(faker instanceof ZodAnyFaker).toBe(true) 21 | }) 22 | 23 | test('ZodAnyFaker.fake should be a function', () => { 24 | const schema = z.any() 25 | const faker = new ZodAnyFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | test('ZodAnyFaker.fake should return any type', () => { 30 | const schema = z.any() 31 | const faker = new ZodAnyFaker(schema) 32 | expectType, any>>(true) 33 | }) 34 | 35 | test('ZodAnyFaker.fake should return a valid data', () => { 36 | install() 37 | const schema = z.any() 38 | const faker = new ZodAnyFaker(schema) 39 | const data = faker.fake() 40 | expect(schema.safeParse(data).success).toBe(true) 41 | }) 42 | -------------------------------------------------------------------------------- /examples/v4/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/v3/zod-void-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodVoidFaker } from '../../src/v3/zod-void-faker' 6 | 7 | test('ZodVoidFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodVoidFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodVoidFaker should accepts a ZodVoid schema', () => { 13 | const schema = z.void() 14 | expect(() => new ZodVoidFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodVoidFaker should return a ZodVoidFaker instance', () => { 18 | const schema = z.void() 19 | const faker = new ZodVoidFaker(schema) 20 | expect(faker instanceof ZodVoidFaker).toBe(true) 21 | }) 22 | 23 | test('ZodVoidFaker.fake should be a function', () => { 24 | const schema = z.void() 25 | const faker = new ZodVoidFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | test('ZodVoidFaker.fake should return void type', () => { 30 | const schema = z.void() 31 | const faker = new ZodVoidFaker(schema) 32 | expectType, void>>(true) 33 | }) 34 | 35 | test('ZodVoidFaker.fake should return a valid data', () => { 36 | install() 37 | const schema = z.void() 38 | const faker = new ZodVoidFaker(schema) 39 | const data = faker.fake() 40 | expect(schema.safeParse(data).success).toBe(true) 41 | }) 42 | -------------------------------------------------------------------------------- /tests/v3/zod-symbol-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { ZodSymbolFaker } from '../../src/v3/zod-symbol-faker' 5 | 6 | test('ZodSymbolFaker should assert parameters', () => { 7 | const invalidSchema = void 0 as any 8 | expect(() => new ZodSymbolFaker(invalidSchema)).toThrow() 9 | }) 10 | 11 | test('ZodSymbolFaker should accepts a ZodSymbol schema', () => { 12 | const schema = z.symbol() 13 | expect(() => new ZodSymbolFaker(schema)).not.toThrow() 14 | }) 15 | 16 | test('ZodSymbolFaker should return a ZodSymbolFaker instance', () => { 17 | const schema = z.symbol() 18 | const faker = new ZodSymbolFaker(schema) 19 | expect(faker instanceof ZodSymbolFaker).toBe(true) 20 | }) 21 | 22 | test('ZodSymbolFaker.fake should be a function', () => { 23 | const schema = z.symbol() 24 | const faker = new ZodSymbolFaker(schema) 25 | expect(typeof faker.fake).toBe('function') 26 | }) 27 | 28 | test('ZodSymbolFaker.fake should return the given type', () => { 29 | const schema = z.symbol() 30 | const faker = new ZodSymbolFaker(schema) 31 | expectType, z.infer>>(true) 32 | }) 33 | 34 | test('ZodSymbolFaker.fake should return a valid data', () => { 35 | const schema = z.symbol() 36 | const faker = new ZodSymbolFaker(schema) 37 | const data = faker.fake() 38 | expect(schema.safeParse(data).success).toBe(true) 39 | }) 40 | -------------------------------------------------------------------------------- /tests/v3/zod-boolean-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { ZodBooleanFaker } from '../../src/v3/zod-boolean-faker' 5 | 6 | test('ZodBooleanFaker should assert parameters', () => { 7 | const invalidSchema = void 0 as any 8 | expect(() => new ZodBooleanFaker(invalidSchema)).toThrow() 9 | }) 10 | 11 | test('ZodBooleanFaker should accepts a ZodBoolean schema', () => { 12 | const schema = z.boolean() 13 | expect(() => new ZodBooleanFaker(schema)).not.toThrow() 14 | }) 15 | 16 | test('ZodBooleanFaker should return a ZodBooleanFaker instance', () => { 17 | const schema = z.boolean() 18 | const faker = new ZodBooleanFaker(schema) 19 | expect(faker instanceof ZodBooleanFaker).toBe(true) 20 | }) 21 | 22 | test('ZodBooleanFaker.fake should be a function', () => { 23 | const schema = z.boolean() 24 | const faker = new ZodBooleanFaker(schema) 25 | expect(typeof faker.fake).toBe('function') 26 | }) 27 | 28 | test('ZodBooleanFaker.fake should return boolean type', () => { 29 | const schema = z.boolean() 30 | const faker = new ZodBooleanFaker(schema) 31 | expectType, boolean>>(true) 32 | }) 33 | 34 | test('ZodBooleanFaker.fake should return a valid data', () => { 35 | const schema = z.boolean() 36 | const faker = new ZodBooleanFaker(schema) 37 | const data = faker.fake() 38 | expect(schema.safeParse(data).success).toBe(true) 39 | }) 40 | -------------------------------------------------------------------------------- /tests/v3/zod-literal-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { ZodLiteralFaker } from '../../src/v3/zod-literal-faker' 5 | 6 | test('ZodLiteralFaker should assert parameters', () => { 7 | const invalidSchema = void 0 as any 8 | expect(() => new ZodLiteralFaker(invalidSchema)).toThrow() 9 | }) 10 | 11 | test('ZodLiteralFaker should accepts a ZodLiteral schema', () => { 12 | const schema = z.literal('foo') 13 | expect(() => new ZodLiteralFaker(schema)).not.toThrow() 14 | }) 15 | 16 | test('ZodLiteralFaker should return a ZodLiteralFaker instance', () => { 17 | const schema = z.literal('foo') 18 | const faker = new ZodLiteralFaker(schema) 19 | expect(faker instanceof ZodLiteralFaker).toBe(true) 20 | }) 21 | 22 | test('ZodLiteralFaker.fake should be a function', () => { 23 | const schema = z.literal('foo') 24 | const faker = new ZodLiteralFaker(schema) 25 | expect(typeof faker.fake).toBe('function') 26 | }) 27 | 28 | test('ZodLiteralFaker.fake should return the given type', () => { 29 | const schema = z.literal('foo') 30 | const faker = new ZodLiteralFaker(schema) 31 | expectType, 'foo'>>(true) 32 | }) 33 | 34 | test('ZodLiteralFaker.fake should return a valid data', () => { 35 | const schema = z.literal('foo') 36 | const faker = new ZodLiteralFaker(schema) 37 | const data = faker.fake() 38 | expect(schema.safeParse(data).success).toBe(true) 39 | }) 40 | -------------------------------------------------------------------------------- /tests/v3/zod-undefined-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { ZodUndefinedFaker } from '../../src/v3/zod-undefined-faker' 5 | 6 | test('ZodUndefinedFaker should assert parameters', () => { 7 | const invalidSchema = void 0 as any 8 | expect(() => new ZodUndefinedFaker(invalidSchema)).toThrow() 9 | }) 10 | 11 | test('ZodUndefinedFaker should accepts a ZodUndefined schema', () => { 12 | const schema = z.undefined() 13 | expect(() => new ZodUndefinedFaker(schema)).not.toThrow() 14 | }) 15 | 16 | test('ZodUndefinedFaker should return a ZodUndefinedFaker instance', () => { 17 | const schema = z.undefined() 18 | const faker = new ZodUndefinedFaker(schema) 19 | expect(faker instanceof ZodUndefinedFaker).toBe(true) 20 | }) 21 | 22 | test('ZodUndefinedFaker.fake should be a function', () => { 23 | const schema = z.undefined() 24 | const faker = new ZodUndefinedFaker(schema) 25 | expect(typeof faker.fake).toBe('function') 26 | }) 27 | 28 | test('ZodUndefinedFaker.fake should return undefined type', () => { 29 | const schema = z.undefined() 30 | const faker = new ZodUndefinedFaker(schema) 31 | expectType, undefined>>(true) 32 | }) 33 | 34 | test('ZodUndefinedFaker.fake should return a valid data', () => { 35 | const schema = z.undefined() 36 | const faker = new ZodUndefinedFaker(schema) 37 | const data = faker.fake() 38 | expect(schema.safeParse(data).success).toBe(true) 39 | }) 40 | -------------------------------------------------------------------------------- /src/v3/zod-date-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { getFaker } from './random' 3 | import { ZodTypeFaker } from './zod-type-faker' 4 | 5 | export const minDateValue = -8640000000000000 6 | export const maxDateValue = 8640000000000000 7 | 8 | export class ZodDateFaker extends ZodTypeFaker { 9 | fake(): z.infer { 10 | let min: undefined | number = undefined 11 | let max: undefined | number = undefined 12 | for (const check of this.schema._def.checks) { 13 | switch (check.kind) { 14 | case 'min': { 15 | const _min = check.value 16 | min = min !== undefined ? Math.max(min, _min) : _min 17 | break 18 | } 19 | case 'max': { 20 | const _max = check.value 21 | max = max !== undefined ? Math.min(max, _max) : _max 22 | break 23 | } 24 | /* v8 ignore next 3 */ 25 | default: { 26 | const _: never = check 27 | } 28 | } 29 | } 30 | if (min === undefined) { 31 | if (getFaker().datatype.boolean({ probability: 0.2 })) { 32 | min = minDateValue 33 | } else { 34 | min = (max ?? new Date('2025-01-01T00:00:00.000Z').getTime()) - 31536000000 35 | } 36 | } 37 | if (max === undefined) { 38 | if (getFaker().datatype.boolean({ probability: 0.2 })) { 39 | max = maxDateValue 40 | } else { 41 | max = min + 31536000000 42 | } 43 | } 44 | 45 | return getFaker().date.between({ from: min, to: max }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/v3/zod-unknown-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodUnknownFaker } from '../../src/v3/zod-unknown-faker' 6 | 7 | test('ZodUnknownFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodUnknownFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodUnknownFaker should accepts a ZodUnknown schema', () => { 13 | const schema = z.unknown() 14 | expect(() => new ZodUnknownFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodUnknownFaker should return a ZodUnknownFaker instance', () => { 18 | const schema = z.unknown() 19 | const faker = new ZodUnknownFaker(schema) 20 | expect(faker instanceof ZodUnknownFaker).toBe(true) 21 | }) 22 | 23 | test('ZodUnknownFaker.fake should be a function', () => { 24 | const schema = z.unknown() 25 | const faker = new ZodUnknownFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | test('ZodUnknownFaker.fake should return unknown type', () => { 30 | const schema = z.unknown() 31 | const faker = new ZodUnknownFaker(schema) 32 | expectType, unknown>>(true) 33 | }) 34 | 35 | test('ZodUnknownFaker.fake should return a valid data', () => { 36 | install() 37 | const schema = z.unknown() 38 | const faker = new ZodUnknownFaker(schema) 39 | const data = faker.fake() 40 | expect(schema.safeParse(data).success).toBe(true) 41 | }) 42 | -------------------------------------------------------------------------------- /examples/v3/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/v3/zod-map-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodMapFaker } from '../../src/v3/zod-map-faker' 6 | 7 | test('ZodMapFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodMapFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodMapFaker should accepts a ZodMap schema', () => { 13 | const schema = z.map(z.number(), z.string()) 14 | expect(() => new ZodMapFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodMapFaker should return a ZodMapFaker instance', () => { 18 | const schema = z.map(z.number(), z.string()) 19 | const faker = new ZodMapFaker(schema) 20 | expect(faker instanceof ZodMapFaker).toBe(true) 21 | }) 22 | 23 | test('ZodMapFaker.fake should be a function', () => { 24 | const schema = z.map(z.number(), z.string()) 25 | const faker = new ZodMapFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | test('ZodMapFaker.fake should return Map type', () => { 30 | const schema = z.map(z.number(), z.string()) 31 | const faker = new ZodMapFaker(schema) 32 | expectType, Map>>(true) 33 | }) 34 | 35 | test('ZodMapFaker.fake should return a valid data', () => { 36 | install() 37 | 38 | const schema = z.map(z.number(), z.string()) 39 | const faker = new ZodMapFaker(schema) 40 | const data = faker.fake() 41 | expect(schema.safeParse(data).success).toBe(true) 42 | }) 43 | -------------------------------------------------------------------------------- /src/v3/zod-object-faker.ts: -------------------------------------------------------------------------------- 1 | import { UnknownKeysParam, z } from 'zod/v3' 2 | import { fake } from './fake' 3 | import { getFaker } from './random' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | export class ZodObjectFaker> extends ZodTypeFaker { 7 | fake(): z.infer { 8 | const result = {} as any 9 | 10 | const catchall = this.schema._def.catchall 11 | const unknownKeys = this.schema._def.unknownKeys as UnknownKeysParam 12 | if (catchall instanceof z.ZodNever) { 13 | switch (unknownKeys) { 14 | case 'passthrough': { 15 | Object.assign( 16 | result, 17 | Object.fromEntries( 18 | getFaker().helpers.multiple(() => [fake(z.string().regex(/^extra_[a-z]{5}$/)), fake(z.any())], { 19 | count: { min: 0, max: 5 }, 20 | }), 21 | ), 22 | ) 23 | break 24 | } 25 | default: { 26 | const _: 'strip' | 'strict' = unknownKeys 27 | } 28 | } 29 | } else { 30 | Object.assign( 31 | result, 32 | Object.fromEntries( 33 | getFaker().helpers.multiple(() => [fake(z.string().regex(/^extra_[a-z]{5}$/)), fake(catchall)], { 34 | count: { min: 0, max: 5 }, 35 | }), 36 | ), 37 | ) 38 | } 39 | 40 | const shape = this.schema._def.shape() 41 | const keys = Object.keys(shape) 42 | for (const key of keys) { 43 | const value = fake(shape[key]) 44 | result[key] = value 45 | } 46 | 47 | return result 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/v3/zod-nullable-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodNullableFaker } from '../../src/v3/zod-nullable-faker' 6 | 7 | test('ZodNullableFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodNullableFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodNullableFaker should accepts a ZodNullable schema', () => { 13 | const schema = z.nullable(z.string()) 14 | expect(() => new ZodNullableFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodNullableFaker should return a ZodNullableFaker instance', () => { 18 | const schema = z.nullable(z.string()) 19 | const faker = new ZodNullableFaker(schema) 20 | expect(faker instanceof ZodNullableFaker).toBe(true) 21 | }) 22 | 23 | test('ZodNullableFaker.fake should be a function', () => { 24 | const schema = z.nullable(z.string()) 25 | const faker = new ZodNullableFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | test('ZodNullableFaker.fake should return nullable type', () => { 30 | const schema = z.nullable(z.string()) 31 | const faker = new ZodNullableFaker(schema) 32 | expectType, string | null>>(true) 33 | }) 34 | 35 | test('ZodNullableFaker.fake should return a valid data', () => { 36 | install() 37 | 38 | const schema = z.nullable(z.string()) 39 | const faker = new ZodNullableFaker(schema) 40 | const data = faker.fake() 41 | expect(schema.safeParse(data).success).toBe(true) 42 | }) 43 | -------------------------------------------------------------------------------- /tests/v3/zod-union-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodUnionFaker } from '../../src/v3/zod-union-faker' 6 | 7 | test('ZodUnionFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodUnionFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodUnionFaker should accepts a ZodUnion schema', () => { 13 | const schema = z.union([z.number(), z.string()]) 14 | expect(() => new ZodUnionFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodUnionFaker should return a ZodUnionFaker instance', () => { 18 | const schema = z.union([z.number(), z.string()]) 19 | const faker = new ZodUnionFaker(schema) 20 | expect(faker instanceof ZodUnionFaker).toBe(true) 21 | }) 22 | 23 | test('ZodUnionFaker.fake should be a function', () => { 24 | const schema = z.union([z.number(), z.string()]) 25 | const faker = new ZodUnionFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | test('ZodUnionFaker.fake should return union type', () => { 30 | const schema = z.union([z.number(), z.string()]) 31 | const faker = new ZodUnionFaker(schema) 32 | expectType, number | string>>(true) 33 | }) 34 | 35 | test('ZodUnionFaker.fake should return a valid data', () => { 36 | install() 37 | 38 | const schema = z.union([z.number(), z.string()]) 39 | const faker = new ZodUnionFaker(schema) 40 | const data = faker.fake() 41 | expect(schema.safeParse(data).success).toBe(true) 42 | }) 43 | -------------------------------------------------------------------------------- /tests/v3/zod-optional-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodOptionalFaker } from '../../src/v3/zod-optional-faker' 6 | 7 | test('ZodOptionalFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodOptionalFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodOptionalFaker should accepts a ZodOptional schema', () => { 13 | const schema = z.optional(z.string()) 14 | expect(() => new ZodOptionalFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodOptionalFaker should return a ZodOptionalFaker instance', () => { 18 | const schema = z.optional(z.string()) 19 | const faker = new ZodOptionalFaker(schema) 20 | expect(faker instanceof ZodOptionalFaker).toBe(true) 21 | }) 22 | 23 | test('ZodOptionalFaker.fake should be a function', () => { 24 | const schema = z.optional(z.string()) 25 | const faker = new ZodOptionalFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | test('ZodOptionalFaker.fake should return optional type', () => { 30 | const schema = z.optional(z.string()) 31 | const faker = new ZodOptionalFaker(schema) 32 | expectType, string | undefined>>(true) 33 | }) 34 | 35 | test('ZodOptionalFaker.fake should return a valid data', () => { 36 | install() 37 | 38 | const schema = z.optional(z.string()) 39 | const faker = new ZodOptionalFaker(schema) 40 | const data = faker.fake() 41 | expect(schema.safeParse(data).success).toBe(true) 42 | }) 43 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/set.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { MAX_DEPTH } from '../config' 3 | import { Context } from '../context' 4 | import { rootFake as internalFake } from '../fake' 5 | import { getFaker } from '../random' 6 | import { Infer } from '../type' 7 | 8 | export function fakeSet(schema: T, context: Context, rootFake: typeof internalFake): Infer { 9 | let min = 0 10 | let max = Infinity 11 | for (const check of (schema._zod.def.checks ?? []) as core.$ZodChecks[]) { 12 | switch (check._zod.def.check) { 13 | case 'max_size': { 14 | max = Math.min(max, check._zod.def.maximum) 15 | break 16 | } 17 | case 'min_size': { 18 | min = Math.max(min, check._zod.def.minimum) 19 | break 20 | } 21 | case 'size_equals': { 22 | min = check._zod.def.size 23 | max = check._zod.def.size 24 | break 25 | } 26 | default: { 27 | const _: 28 | | 'bigint_format' 29 | | 'greater_than' 30 | | 'length_equals' 31 | | 'less_than' 32 | | 'max_length' 33 | | 'mime_type' 34 | | 'min_length' 35 | | 'multiple_of' 36 | | 'number_format' 37 | | 'overwrite' 38 | | 'property' 39 | | 'string_format' 40 | | never = check._zod.def.check 41 | break 42 | } 43 | } 44 | } 45 | 46 | max = max === Infinity ? min + getFaker().number.int({ min: 0, max: 10 }) : max 47 | if (context.depth > MAX_DEPTH) { 48 | max = min 49 | } 50 | return new Set( 51 | getFaker().helpers.multiple(() => rootFake(schema._zod.def.valueType, context), { count: { min, max } }), 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { renameSync } from 'node:fs' 3 | import { dirname, resolve } from 'node:path' 4 | import { fileURLToPath } from 'node:url' 5 | import { defineConfig } from 'vite' 6 | import dts from 'vite-plugin-dts' 7 | 8 | const __dirname = dirname(fileURLToPath(import.meta.url)) 9 | 10 | export default defineConfig({ 11 | build: { 12 | lib: { 13 | entry: { 14 | '.': resolve(__dirname, 'src/v3/index.ts'), 15 | v3: resolve(__dirname, 'src/v3/index.ts'), 16 | v4: resolve(__dirname, 'src/v4/index.ts'), 17 | }, 18 | name: 'ZodSchemaFaker', 19 | fileName: 'zod-schema-faker', 20 | formats: ['es', 'cjs'], 21 | }, 22 | rollupOptions: { 23 | external: ['zod/v3', 'zod/v4/core'], 24 | output: [ 25 | { 26 | format: 'es', 27 | entryFileNames: '[name]/zod-schema-faker.es.js', 28 | }, 29 | { 30 | format: 'cjs', 31 | entryFileNames: '[name]/zod-schema-faker.cjs', 32 | }, 33 | ], 34 | }, 35 | }, 36 | plugins: [ 37 | dts({ 38 | rollupTypes: true, 39 | afterBuild() { 40 | const map = { 41 | 'dist/..d.ts': 'dist/zod-schema-faker.d.ts', 42 | 'dist/v3.d.ts': 'dist/v3/zod-schema-faker.d.ts', 43 | 'dist/v4.d.ts': 'dist/v4/zod-schema-faker.d.ts', 44 | } 45 | for (const [key, value] of Object.entries(map)) { 46 | renameSync(resolve(__dirname, key), resolve(__dirname, value)) 47 | } 48 | }, 49 | }), 50 | ], 51 | test: { 52 | coverage: { 53 | include: ['src/**/*'], 54 | thresholds: { 55 | '100': true, 56 | }, 57 | }, 58 | }, 59 | }) 60 | -------------------------------------------------------------------------------- /src/v3/zod-bigint-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { getFaker } from './random' 3 | import { lcm } from './utils' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | export class ZodBigIntFaker extends ZodTypeFaker { 7 | fake(): z.infer { 8 | let min: undefined | bigint = undefined 9 | let max: undefined | bigint = undefined 10 | let multipleOf: bigint = 1n 11 | for (const check of this.schema._def.checks) { 12 | switch (check.kind) { 13 | case 'min': { 14 | const _min = check.value + (check.inclusive ? 0n : 1n) 15 | min = min !== undefined ? (min > _min ? min : _min) : _min 16 | break 17 | } 18 | case 'max': { 19 | const _max = check.value - (check.inclusive ? 0n : 1n) 20 | max = max !== undefined ? (max < _max ? max : _max) : _max 21 | break 22 | } 23 | case 'multipleOf': { 24 | const _multipleOf = check.value < 0n ? -check.value : check.value 25 | multipleOf = lcm(multipleOf, _multipleOf) 26 | break 27 | } 28 | /* v8 ignore next 3 */ 29 | default: { 30 | const _: never = check 31 | } 32 | } 33 | } 34 | const largeThanMultipleOf = multipleOf * 1000n 35 | if (min !== undefined && max !== undefined) { 36 | if (min > max) { 37 | throw new RangeError() 38 | } 39 | } else if (min !== undefined) { 40 | max = min + largeThanMultipleOf 41 | } else if (max !== undefined) { 42 | min = max - largeThanMultipleOf 43 | } else { 44 | min = -largeThanMultipleOf 45 | max = largeThanMultipleOf 46 | } 47 | return getFaker().number.bigInt({ min, max, multipleOf }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | lint: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v6 15 | 16 | - uses: actions/setup-node@v6 17 | with: 18 | node-version-file: '.nvmrc' 19 | 20 | - run: npm ci 21 | 22 | - run: npx prettier --check . 23 | 24 | test: 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - uses: actions/checkout@v6 29 | 30 | - uses: actions/setup-node@v6 31 | with: 32 | node-version-file: '.nvmrc' 33 | 34 | - run: npm ci 35 | 36 | - run: | 37 | npx vitest run 38 | npm run build 39 | npm run pack 40 | cd e2e/issue-189/v3 && npm ci && npm i ../../../zod-schema-faker.tgz && npm run test && cd - 41 | cd e2e/issue-189/v4 && npm ci && npm i ../../../zod-schema-faker.tgz && npm run test && cd - 42 | cd examples/v3 && npm ci && npm i ../../zod-schema-faker.tgz && npm run build && cd - 43 | cd examples/v4 && npm ci && npm i ../../zod-schema-faker.tgz && npm run build && cd - 44 | 45 | build: 46 | runs-on: ubuntu-latest 47 | 48 | needs: 49 | - lint 50 | - test 51 | 52 | steps: 53 | - uses: actions/checkout@v6 54 | 55 | - uses: actions/setup-node@v6 56 | with: 57 | node-version-file: '.nvmrc' 58 | 59 | - run: npm ci 60 | 61 | - run: npm run build 62 | 63 | - uses: JS-DevTools/npm-publish@v4 64 | id: publish 65 | if: ${{ github.ref == 'refs/heads/main' }} 66 | with: 67 | token: ${{ secrets.NPM_TOKEN }} 68 | package: ./package.json 69 | access: public 70 | dry-run: false 71 | strategy: upgrade 72 | -------------------------------------------------------------------------------- /tests/v3/zod-readonly-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { fake, install } from '../../src/v3' 5 | 6 | test('freeze', () => { 7 | const schema = z.object({ key: z.string() }).readonly() 8 | 9 | install() 10 | const data = fake(schema) 11 | 12 | expect(() => { 13 | // @ts-expect-error 14 | data.key = '' 15 | }).toThrow() 16 | }) 17 | 18 | test('object', () => { 19 | const schema = z.object({ key: z.string() }).readonly() 20 | 21 | install() 22 | const data = fake(schema) 23 | 24 | expectType>(true) 25 | expect(schema.safeParse(data).success).toBe(true) 26 | }) 27 | 28 | test('array', () => { 29 | const schema = z.array(z.string()).readonly() 30 | 31 | install() 32 | const data = fake(schema) 33 | 34 | expectType>>(true) 35 | expect(schema.safeParse(data).success).toBe(true) 36 | }) 37 | 38 | test('tuple', () => { 39 | const schema = z.tuple([z.string(), z.number()]).readonly() 40 | 41 | install() 42 | const data = fake(schema) 43 | 44 | expectType>(true) 45 | expect(schema.safeParse(data).success).toBe(true) 46 | }) 47 | 48 | test('map', () => { 49 | const schema = z.map(z.string(), z.date()).readonly() 50 | 51 | install() 52 | const data = fake(schema) 53 | 54 | expectType>>(true) 55 | expect(schema.safeParse(data).success).toBe(true) 56 | }) 57 | 58 | test('set', () => { 59 | const schema = z.set(z.string()).readonly() 60 | 61 | install() 62 | const data = fake(schema) 63 | 64 | expectType>>(true) 65 | expect(schema.safeParse(data).success).toBe(true) 66 | }) 67 | -------------------------------------------------------------------------------- /tests/v3/zod-record-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodRecordFaker } from '../../src/v3/zod-record-faker' 6 | 7 | test('ZodRecordFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodRecordFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodRecordFaker should assert type of key', () => { 13 | install() 14 | 15 | const numericalKeySchema = z.record(z.number(), z.any()) 16 | const faker = new ZodRecordFaker(numericalKeySchema) 17 | expect(() => faker.fake()).toThrow() 18 | }) 19 | 20 | test('ZodRecordFaker should accepts a ZodRecord schema', () => { 21 | const schema = z.record(z.string(), z.number()) 22 | expect(() => new ZodRecordFaker(schema)).not.toThrow() 23 | }) 24 | 25 | test('ZodRecordFaker should return a ZodRecordFaker instance', () => { 26 | const schema = z.record(z.string(), z.number()) 27 | const faker = new ZodRecordFaker(schema) 28 | expect(faker instanceof ZodRecordFaker).toBe(true) 29 | }) 30 | 31 | test('ZodRecordFaker.fake should be a function', () => { 32 | const schema = z.record(z.string(), z.number()) 33 | const faker = new ZodRecordFaker(schema) 34 | expect(typeof faker.fake).toBe('function') 35 | }) 36 | 37 | test('ZodRecordFaker.fake should return record type', () => { 38 | const schema = z.record(z.string(), z.number()) 39 | const faker = new ZodRecordFaker(schema) 40 | expectType, Record>>(true) 41 | }) 42 | 43 | test('ZodRecordFaker.fake should return a valid data', () => { 44 | install() 45 | 46 | const schema = z.record(z.string(), z.number()) 47 | const faker = new ZodRecordFaker(schema) 48 | const data = faker.fake() 49 | expect(schema.safeParse(data).success).toBe(true) 50 | }) 51 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/array.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { MAX_DEPTH } from '../config' 3 | import { Context } from '../context' 4 | import { rootFake as internalFake } from '../fake' 5 | import { getFaker } from '../random' 6 | import { Infer } from '../type' 7 | 8 | export function fakeArray( 9 | schema: T, 10 | context: Context, 11 | rootFake: typeof internalFake, 12 | ): Infer { 13 | let min: undefined | number = undefined 14 | let max: undefined | number = undefined 15 | let length: undefined | number = undefined 16 | for (const check of (schema._zod.def.checks ?? []) as core.$ZodChecks[]) { 17 | switch (check._zod.def.check) { 18 | case 'length_equals': { 19 | if (length !== undefined && length !== check._zod.def.length) { 20 | throw new RangeError() 21 | } 22 | length = check._zod.def.length 23 | break 24 | } 25 | case 'max_length': { 26 | max = Math.min(max ?? Infinity, check._zod.def.maximum) 27 | break 28 | } 29 | case 'min_length': { 30 | min = Math.max(min ?? 0, check._zod.def.minimum) 31 | break 32 | } 33 | default: { 34 | const _: 35 | | 'bigint_format' 36 | | 'greater_than' 37 | | 'less_than' 38 | | 'max_size' 39 | | 'mime_type' 40 | | 'min_size' 41 | | 'multiple_of' 42 | | 'number_format' 43 | | 'overwrite' 44 | | 'property' 45 | | 'size_equals' 46 | | 'string_format' 47 | | never = check._zod.def.check 48 | break 49 | } 50 | } 51 | } 52 | 53 | min = min ?? length ?? 0 54 | max = max ?? length ?? min + 3 55 | if (context.depth > MAX_DEPTH) { 56 | max = min 57 | } 58 | return getFaker().helpers.multiple(() => rootFake(schema._zod.def.element, context), { count: { min, max } }) 59 | } 60 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/big-int.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | import { lcm } from '../utils' 7 | 8 | export function fakeBigInt( 9 | schema: T, 10 | context: Context, 11 | rootFake: typeof internalFake, 12 | ): Infer { 13 | let min = undefined 14 | let max = undefined 15 | let multipleOf = 1n 16 | for (const check of (schema._zod.def.checks ?? []) as core.$ZodChecks[]) { 17 | switch (check._zod.def.check) { 18 | case 'greater_than': { 19 | const value = BigInt(check._zod.def.value as any) 20 | const _min = value + (check._zod.def.inclusive ? 0n : 1n) 21 | min = min !== undefined ? (min > _min ? min : _min) : _min 22 | break 23 | } 24 | case 'less_than': { 25 | const value = BigInt(check._zod.def.value as any) 26 | const _max = value - (check._zod.def.inclusive ? 0n : 1n) 27 | max = max !== undefined ? (max < _max ? max : _max) : _max 28 | break 29 | } 30 | case 'multiple_of': { 31 | const value = BigInt(check._zod.def.value as any) 32 | const _multipleOf = value < 0n ? -value : value 33 | multipleOf = lcm(multipleOf, _multipleOf) 34 | break 35 | } 36 | default: { 37 | const _: 38 | | 'bigint_format' 39 | | 'length_equals' 40 | | 'max_length' 41 | | 'max_size' 42 | | 'mime_type' 43 | | 'min_length' 44 | | 'min_size' 45 | | 'number_format' 46 | | 'overwrite' 47 | | 'property' 48 | | 'size_equals' 49 | | 'string_format' 50 | | never = check._zod.def.check 51 | break 52 | } 53 | } 54 | } 55 | if (min === undefined) { 56 | if (max === undefined) { 57 | min = 0n 58 | } else { 59 | min = max - BigInt(10n) 60 | } 61 | } 62 | return getFaker().number.bigInt({ min, max, multipleOf }) 63 | } 64 | -------------------------------------------------------------------------------- /tests/v3/zod-enum-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodEnumFaker } from '../../src/v3/zod-enum-faker' 6 | 7 | test('ZodEnumFaker should assert parameters', () => { 8 | const schema = void 0 as any 9 | expect(() => new ZodEnumFaker(schema)).toThrow() 10 | }) 11 | 12 | test('ZodEnumFaker should accepts a ZodEnum schema', () => { 13 | const schema = z.enum(['foo', 'bar']) 14 | expect(() => new ZodEnumFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodEnumFaker should return a ZodEnumFaker instance', () => { 18 | const schema = z.enum(['foo', 'bar']) 19 | const faker = new ZodEnumFaker(schema) 20 | expect(faker instanceof ZodEnumFaker).toBe(true) 21 | }) 22 | 23 | test('ZodEnumFaker.fake should be a function', () => { 24 | const schema = z.enum(['foo', 'bar']) 25 | const faker = new ZodEnumFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | test('ZodEnumFaker.fake should return the given type', () => { 30 | const schema = z.enum(['foo', 'bar']) 31 | const faker = new ZodEnumFaker(schema) 32 | expectType, 'foo' | 'bar'>>(true) 33 | }) 34 | 35 | test('ZodEnumFaker.fake should return a valid data', () => { 36 | install() 37 | 38 | const schema = z.enum(['foo', 'bar']) 39 | const faker = new ZodEnumFaker(schema) 40 | const data = faker.fake() 41 | expect(schema.safeParse(data).success).toBe(true) 42 | }) 43 | 44 | test('ZodEnumFaker.fake.extract should return a valid data', () => { 45 | install() 46 | 47 | const schema = z.enum(['foo', 'bar']).extract(['foo']) 48 | const faker = new ZodEnumFaker(schema) 49 | const data = faker.fake() 50 | expect(schema.safeParse(data).success).toBe(true) 51 | }) 52 | 53 | test('ZodEnumFaker.fake.exclude should return a valid data', () => { 54 | install() 55 | 56 | const schema = z.enum(['foo', 'bar']).exclude(['foo']) 57 | const faker = new ZodEnumFaker(schema) 58 | const data = faker.fake() 59 | expect(schema.safeParse(data).success).toBe(true) 60 | }) 61 | -------------------------------------------------------------------------------- /src/v3/random.ts: -------------------------------------------------------------------------------- 1 | import { Faker, fakerEN, SimpleFaker } from '@faker-js/faker' 2 | import RandExp from 'randexp' 3 | 4 | let _faker: Faker = fakerEN 5 | 6 | /** 7 | * Use given faker instance instead of the default one. 8 | * 9 | * @see https://fakerjs.dev/guide/localization.html for more information. 10 | */ 11 | export function setFaker(faker: Faker): void { 12 | _faker = faker 13 | } 14 | 15 | /** 16 | * Get the current faker instance. 17 | * 18 | * @default fakerEN 19 | */ 20 | export function getFaker(): Faker { 21 | return _faker 22 | } 23 | 24 | const _simpleFaker = new SimpleFaker() 25 | /** 26 | * Create random strings that match a given regular expression. 27 | */ 28 | export const randexp = (pattern: string | RegExp, flags?: string): string => { 29 | const randexp = new RandExp(pattern, flags) 30 | randexp.randInt = (from, to) => _simpleFaker.number.int({ min: from, max: to }) 31 | return randexp.gen() 32 | } 33 | 34 | /** 35 | * Sets the seed or generates a new one. 36 | * 37 | * This method is intended to allow for consistent values in tests, so you might want to use hardcoded values as the seed. 38 | */ 39 | export const seed = (value?: number): void => { 40 | _faker.seed(value) 41 | _simpleFaker.seed(value) 42 | } 43 | 44 | /** 45 | * This exists for compatibility with the previous version. Will be removed in next major version. 46 | * 47 | * @deprecated Use {@link setFaker} instead. 48 | * 49 | * @todo Remove in next major version. 50 | */ 51 | export const installFaker: typeof setFaker = setFaker 52 | 53 | /** 54 | * This exists for compatibility with the previous version. Will be removed in next major version. 55 | * 56 | * @deprecated Use {@link getFaker} instead. 57 | * 58 | * @todo Remove in next major version. 59 | */ 60 | export const runFake = any>( 61 | runner: Awaited> extends ReturnType ? Runner : never, 62 | ): ReturnType => { 63 | const result = runner(_faker) 64 | if (result instanceof Promise) { 65 | throw new SyntaxError('InternalError: runFake cannot be used with async functions') 66 | } 67 | 68 | return result 69 | } 70 | -------------------------------------------------------------------------------- /src/v3/zod-number-faker.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { getFaker } from './random' 3 | import { lcm } from './utils' 4 | import { ZodTypeFaker } from './zod-type-faker' 5 | 6 | export class ZodNumberFaker extends ZodTypeFaker { 7 | fake(): z.infer { 8 | let min: undefined | number = undefined 9 | let max: undefined | number = undefined 10 | let multipleOf: undefined | number = undefined 11 | let int: boolean = false 12 | let finite: boolean = false 13 | for (const check of this.schema._def.checks) { 14 | switch (check.kind) { 15 | case 'min': { 16 | const _min = check.value + (check.inclusive ? 0 : 0.000000000000001) 17 | min = min !== undefined ? Math.max(min, _min) : _min 18 | break 19 | } 20 | case 'max': { 21 | const _max = check.value - (check.inclusive ? 0 : 0.000000000000001) 22 | max = max !== undefined ? Math.min(max, _max) : _max 23 | break 24 | } 25 | case 'multipleOf': { 26 | const _multipleOf = check.value 27 | multipleOf = multipleOf !== undefined ? lcm(multipleOf, _multipleOf) : _multipleOf 28 | break 29 | } 30 | case 'int': 31 | int = true 32 | break 33 | case 'finite': 34 | finite = true 35 | break 36 | /* v8 ignore next 3 */ 37 | default: { 38 | const _: never = check 39 | } 40 | } 41 | } 42 | 43 | if (finite === false && int === false && multipleOf === undefined) { 44 | if (min === undefined && getFaker().datatype.boolean({ probability: 0.2 })) { 45 | return -Infinity 46 | } 47 | if (max === undefined && getFaker().datatype.boolean({ probability: 0.2 })) { 48 | return Infinity 49 | } 50 | } 51 | 52 | min ??= Number.MIN_SAFE_INTEGER 53 | max ??= Number.MAX_SAFE_INTEGER 54 | int = int || (multipleOf !== undefined && multipleOf % 1 === 0) 55 | if (min > max) { 56 | throw new RangeError() 57 | } 58 | const method = int ? 'int' : 'float' 59 | return getFaker().number[method]({ min, max, multipleOf }) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/v3/zod-catch-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodCatchFaker } from '../../src/v3/zod-catch-faker' 6 | 7 | test('ZodCatchFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodCatchFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodCatchFaker should accepts a ZodCatch schema', () => { 13 | const schema = z.number().catch(42) 14 | expect(() => new ZodCatchFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodCatchFaker should return a ZodCatchFaker instance', () => { 18 | const schema = z.number().catch(42) 19 | const faker = new ZodCatchFaker(schema) 20 | expect(faker instanceof ZodCatchFaker).toBe(true) 21 | }) 22 | 23 | test('ZodCatchFaker.fake should be a function', () => { 24 | const schema = z.number().catch(42) 25 | const faker = new ZodCatchFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | test('ZodCatchFaker.fake should return catch type', () => { 30 | const schema = z.number().catch(42) 31 | const faker = new ZodCatchFaker(schema) 32 | expectType, z.infer>>(true) 33 | }) 34 | 35 | test('ZodCatchFaker.fake should return a valid data', () => { 36 | install() 37 | const schema = z.number().catch(42) 38 | const faker = new ZodCatchFaker(schema) 39 | const data = faker.fake() 40 | expect(schema.safeParse(data).success).toBe(true) 41 | }) 42 | 43 | test('ZodCatchFaker.fake should sometimes return catch value', () => { 44 | install() 45 | const schema = z.number().catch(42) 46 | const faker = new ZodCatchFaker(schema) 47 | while (true) { 48 | const data = faker.fake() 49 | if (data === 42) { 50 | return 51 | } 52 | } 53 | }) 54 | 55 | test('ZodCatchFaker.fake should sometimes not return catch value', () => { 56 | install() 57 | const schema = z.number().catch(42) 58 | const faker = new ZodCatchFaker(schema) 59 | while (true) { 60 | const data = faker.fake() 61 | if (data !== 42) { 62 | return 63 | } 64 | } 65 | }) 66 | -------------------------------------------------------------------------------- /tests/v3/zod-function-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodFunctionFaker } from '../../src/v3/zod-function-faker' 6 | 7 | test('ZodFunctionFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodFunctionFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodFunctionFaker should accepts a ZodFunction schema', () => { 13 | const schema = z.function(z.tuple([z.number(), z.string()]), z.boolean()) 14 | expect(() => new ZodFunctionFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodFunctionFaker should return a ZodFunctionFaker instance', () => { 18 | const schema = z.function(z.tuple([z.number(), z.string()]), z.boolean()) 19 | const faker = new ZodFunctionFaker(schema) 20 | expect(faker instanceof ZodFunctionFaker).toBe(true) 21 | }) 22 | 23 | test('ZodFunctionFaker.fake should be a function', () => { 24 | const schema = z.function(z.tuple([z.number(), z.string()]), z.boolean()) 25 | const faker = new ZodFunctionFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | test('ZodFunctionFaker.fake should return function type', () => { 30 | const schema = z.function(z.tuple([z.number(), z.string()]), z.boolean()) 31 | const faker = new ZodFunctionFaker(schema) 32 | expectType, (_: number, __: string) => boolean>>(true) 33 | }) 34 | 35 | test('ZodFunctionFaker.fake should return a valid data', () => { 36 | install() 37 | 38 | const schema = z.function(z.tuple([z.number(), z.string()]), z.boolean()) 39 | const faker = new ZodFunctionFaker(schema) 40 | const data = faker.fake() 41 | expect(schema.safeParse(data).success).toBe(true) 42 | }) 43 | 44 | test('ZodFunctionFaker.fake when the returned function is called, it should return a valid data', () => { 45 | install() 46 | 47 | const schema = z.function(z.tuple([z.number(), z.string()]), z.boolean()) 48 | const faker = new ZodFunctionFaker(schema) 49 | const fn = faker.fake() 50 | expect(schema.safeParse(fn).success).toBe(true) 51 | expect(z.boolean().safeParse(fn(1, '')).success).toBe(true) 52 | }) 53 | -------------------------------------------------------------------------------- /tests/v3/zod-lazy-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodLazyFaker } from '../../src/v3/zod-lazy-faker' 6 | import { testMultipleTimes } from './util' 7 | 8 | interface Category { 9 | name: string 10 | subcategories: Category[] 11 | } 12 | 13 | test('ZodLazyFaker should assert parameters', () => { 14 | const invalidSchema = void 0 as any 15 | expect(() => new ZodLazyFaker(invalidSchema)).toThrow() 16 | }) 17 | 18 | test('ZodLazyFaker should accepts a ZodLazy schema', () => { 19 | const schema = z.lazy(() => 20 | z.object({ 21 | name: z.string(), 22 | subcategories: z.array(schema), 23 | }), 24 | ) as z.ZodType 25 | expect(() => new ZodLazyFaker(schema)).not.toThrow() 26 | }) 27 | 28 | test('ZodLazyFaker should return a ZodLazyFaker instance', () => { 29 | const schema = z.lazy(() => 30 | z.object({ 31 | name: z.string(), 32 | subcategories: z.array(schema), 33 | }), 34 | ) as z.ZodType 35 | const faker = new ZodLazyFaker(schema) 36 | expect(faker instanceof ZodLazyFaker).toBe(true) 37 | }) 38 | 39 | test('ZodLazyFaker.fake should be a function', () => { 40 | const schema = z.lazy(() => 41 | z.object({ 42 | name: z.string(), 43 | subcategories: z.array(schema), 44 | }), 45 | ) as z.ZodType 46 | const faker = new ZodLazyFaker(schema) 47 | expect(typeof faker.fake).toBe('function') 48 | }) 49 | 50 | test('ZodLazyFaker.fake should return the given type', () => { 51 | const schema = z.lazy(() => 52 | z.object({ 53 | name: z.string(), 54 | subcategories: z.array(schema), 55 | }), 56 | ) as z.ZodType 57 | const faker = new ZodLazyFaker(schema) 58 | expectType, Category>>(true) 59 | }) 60 | 61 | testMultipleTimes('ZodLazyFaker.fake should return a valid data', () => { 62 | install() 63 | 64 | const schema = z.lazy(() => 65 | z.object({ 66 | name: z.string(), 67 | subcategories: z.array(schema), 68 | }), 69 | ) as z.ZodType 70 | const faker = new ZodLazyFaker(schema) 71 | const data = faker.fake() 72 | expect(schema.safeParse(data).success).toBe(true) 73 | }) 74 | -------------------------------------------------------------------------------- /tests/v3/zod-default-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodDefaultFaker } from '../../src/v3/zod-default-faker' 6 | 7 | const defaultData = { foo: 'bar' } 8 | 9 | test('ZodDefaultFaker should assert parameters', () => { 10 | const invalidSchema = void 0 as any 11 | expect(() => new ZodDefaultFaker(invalidSchema)).toThrow() 12 | }) 13 | 14 | test('ZodDefaultFaker should accepts a ZodDefault schema', () => { 15 | const schema = z.object({}).default(defaultData) 16 | expect(() => new ZodDefaultFaker(schema)).not.toThrow() 17 | }) 18 | 19 | test('ZodDefaultFaker should return a ZodDefaultFaker instance', () => { 20 | const schema = z.object({}).default(defaultData) 21 | const faker = new ZodDefaultFaker(schema) 22 | expect(faker instanceof ZodDefaultFaker).toBe(true) 23 | }) 24 | 25 | test('ZodDefaultFaker.fake should be a function', () => { 26 | const schema = z.object({}).default(defaultData) 27 | const faker = new ZodDefaultFaker(schema) 28 | expect(typeof faker.fake).toBe('function') 29 | }) 30 | 31 | test('ZodDefaultFaker.fake should return object type', () => { 32 | const schema = z.object({}).default(defaultData) 33 | const faker = new ZodDefaultFaker(schema) 34 | expectType, {}>>(true) 35 | }) 36 | 37 | test('ZodDefaultFaker.fake should sometimes return defaultData', () => { 38 | install() 39 | 40 | const schema = z.object({}).default(defaultData) 41 | const faker = new ZodDefaultFaker(schema) 42 | 43 | let i = 0 44 | while (++i < 1e3) { 45 | const data = faker.fake() 46 | expect(schema.safeParse(data).success).toBe(true) 47 | if (data === defaultData) { 48 | return 49 | } 50 | } 51 | 52 | throw Error('should not reach here') 53 | }) 54 | 55 | test('ZodDefaultFaker.fake should sometimes return non-defaultData', () => { 56 | install() 57 | 58 | const schema = z.object({}).default(defaultData) 59 | const faker = new ZodDefaultFaker(schema) 60 | 61 | let i = 0 62 | while (++i < 1e3) { 63 | const data = faker.fake() 64 | expect(schema.safeParse(data).success).toBe(true) 65 | if (data !== defaultData) { 66 | return 67 | } 68 | } 69 | 70 | throw Error('should not reach here') 71 | }) 72 | -------------------------------------------------------------------------------- /e2e/issue-189/v3/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "issue-189", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "issue-189", 8 | "dependencies": { 9 | "zod": "3.25.76" 10 | }, 11 | "devDependencies": { 12 | "@types/node": "24.10.4", 13 | "ts-expect": "1.3.0", 14 | "typescript": "5.9.3" 15 | } 16 | }, 17 | "node_modules/@types/node": { 18 | "version": "24.10.4", 19 | "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", 20 | "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", 21 | "dev": true, 22 | "license": "MIT", 23 | "dependencies": { 24 | "undici-types": "~7.16.0" 25 | } 26 | }, 27 | "node_modules/ts-expect": { 28 | "version": "1.3.0", 29 | "resolved": "https://registry.npmjs.org/ts-expect/-/ts-expect-1.3.0.tgz", 30 | "integrity": "sha512-e4g0EJtAjk64xgnFPD6kTBUtpnMVzDrMb12N1YZV0VvSlhnVT3SGxiYTLdGy8Q5cYHOIC/FAHmZ10eGrAguicQ==", 31 | "dev": true 32 | }, 33 | "node_modules/typescript": { 34 | "version": "5.9.3", 35 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", 36 | "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", 37 | "dev": true, 38 | "license": "Apache-2.0", 39 | "bin": { 40 | "tsc": "bin/tsc", 41 | "tsserver": "bin/tsserver" 42 | }, 43 | "engines": { 44 | "node": ">=14.17" 45 | } 46 | }, 47 | "node_modules/undici-types": { 48 | "version": "7.16.0", 49 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", 50 | "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", 51 | "dev": true, 52 | "license": "MIT" 53 | }, 54 | "node_modules/zod": { 55 | "version": "3.25.76", 56 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 57 | "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 58 | "license": "MIT", 59 | "funding": { 60 | "url": "https://github.com/sponsors/colinhacks" 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zod-schema-faker", 3 | "version": "2.0.2", 4 | "description": "Generates mock data from zod schema. Powered by @faker-js/faker and randexp.js", 5 | "keywords": [ 6 | "zod", 7 | "faker-js", 8 | "fake", 9 | "stub", 10 | "mock", 11 | "test", 12 | "json", 13 | "schema" 14 | ], 15 | "homepage": "https://github.com/soc221b/zod-schema-faker#readme", 16 | "bugs": { 17 | "url": "https://github.com/soc221b/zod-schema-faker/issues" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/soc221b/zod-schema-faker.git" 22 | }, 23 | "license": "MIT", 24 | "author": "Ernest ", 25 | "sideEffects": false, 26 | "type": "module", 27 | "exports": { 28 | ".": { 29 | "types": "./dist/zod-schema-faker.d.ts", 30 | "import": "./dist/zod-schema-faker.es.js", 31 | "require": "./dist/zod-schema-faker.cjs" 32 | }, 33 | "./v3": { 34 | "types": "./dist/v3/zod-schema-faker.d.ts", 35 | "import": "./dist/v3/zod-schema-faker.es.js", 36 | "require": "./dist/v3/zod-schema-faker.cjs" 37 | }, 38 | "./v4": { 39 | "types": "./dist/v4/zod-schema-faker.d.ts", 40 | "import": "./dist/v4/zod-schema-faker.es.js", 41 | "require": "./dist/v4/zod-schema-faker.cjs" 42 | } 43 | }, 44 | "main": "./dist/zod-schema-faker.cjs", 45 | "module": "./dist/zod-schema-faker.es.js", 46 | "types": "./dist/zod-schema-faker.d.ts", 47 | "files": [ 48 | "dist" 49 | ], 50 | "scripts": { 51 | "build": "tsc && vite build", 52 | "format": "prettier --write .", 53 | "pack": "rm -f *.tgz && npm pack && mv zod-schema-faker* zod-schema-faker.tgz", 54 | "test": "vitest" 55 | }, 56 | "dependencies": { 57 | "@faker-js/faker": "10.1.0", 58 | "randexp": "0.5.3" 59 | }, 60 | "devDependencies": { 61 | "@types/node": "24.10.4", 62 | "@vitest/coverage-v8": "4.0.16", 63 | "prettier": "3.7.4", 64 | "prettier-plugin-organize-imports": "4.3.0", 65 | "prettier-plugin-packagejson": "2.5.20", 66 | "prettier-plugin-sort-json": "4.1.1", 67 | "rimraf": "6.1.2", 68 | "ts-expect": "1.3.0", 69 | "ts-node": "10.9.2", 70 | "typescript": "5.9.3", 71 | "vite": "7.3.0", 72 | "vite-plugin-dts": "4.5.4", 73 | "vitest": "4.0.16", 74 | "zod": "4.1.12" 75 | }, 76 | "peerDependencies": { 77 | "zod": "^3.25.0 || ^4.0.0" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tests/v3/zod-tuple-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { describe, expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodTupleFaker } from '../../src/v3/zod-tuple-faker' 6 | 7 | test('ZodTupleFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodTupleFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodTupleFaker should accepts a ZodTuple schema', () => { 13 | const schema = z.tuple([z.number(), z.string()]).rest(z.boolean()) 14 | expect(() => new ZodTupleFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodTupleFaker should return a ZodTupleFaker instance', () => { 18 | const schema = z.tuple([z.number(), z.string()]).rest(z.boolean()) 19 | const faker = new ZodTupleFaker(schema) 20 | expect(faker instanceof ZodTupleFaker).toBe(true) 21 | }) 22 | 23 | test('ZodTupleFaker.fake should be a function', () => { 24 | const schema = z.tuple([z.number(), z.string()]).rest(z.boolean()) 25 | const faker = new ZodTupleFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | describe('without rest', () => { 30 | test('ZodTupleFaker.fake should return tuple type', () => { 31 | const schema = z.tuple([z.number(), z.string()]) 32 | const faker = new ZodTupleFaker(schema) 33 | expectType['0'], number>>(true) 34 | expectType['1'], string>>(true) 35 | }) 36 | 37 | test('ZodTupleFaker.fake should return a valid data', () => { 38 | install() 39 | 40 | const schema = z.tuple([z.number(), z.string()]) 41 | const faker = new ZodTupleFaker(schema) 42 | const data = faker.fake() 43 | expect(schema.safeParse(data).success).toBe(true) 44 | }) 45 | }) 46 | 47 | describe('rest', () => { 48 | test('ZodTupleFaker.fake should return tuple type', () => { 49 | const schema = z.tuple([z.number(), z.string()]).rest(z.boolean()) 50 | const faker = new ZodTupleFaker(schema) 51 | expectType, [number, string, ...boolean[]]>>(true) 52 | }) 53 | 54 | test('ZodTupleFaker.fake should return a valid data', () => { 55 | install() 56 | 57 | const schema = z.tuple([z.number(), z.string()]).rest(z.boolean()) 58 | const faker = new ZodTupleFaker(schema) 59 | const data = faker.fake() 60 | expect(schema.safeParse(data).success).toBe(true) 61 | }) 62 | }) 63 | -------------------------------------------------------------------------------- /tests/v3/zod-discriminated-union-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodDiscriminatedUnionFaker } from '../../src/v3/zod-discriminated-union-faker' 6 | 7 | test('ZodDiscriminatedUnionFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodDiscriminatedUnionFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodDiscriminatedUnionFaker should accepts a ZodDiscriminatedUnion schema', () => { 13 | const schema = z.discriminatedUnion('type', [ 14 | z.object({ type: z.literal('a'), a: z.string() }), 15 | z.object({ type: z.literal('b'), b: z.string() }), 16 | ]) 17 | expect(() => new ZodDiscriminatedUnionFaker(schema)).not.toThrow() 18 | }) 19 | 20 | test('ZodDiscriminatedUnionFaker should return a ZodDiscriminatedUnionFaker instance', () => { 21 | const schema = z.discriminatedUnion('type', [ 22 | z.object({ type: z.literal('a'), a: z.string() }), 23 | z.object({ type: z.literal('b'), b: z.string() }), 24 | ]) 25 | const faker = new ZodDiscriminatedUnionFaker(schema) 26 | expect(faker instanceof ZodDiscriminatedUnionFaker).toBe(true) 27 | }) 28 | 29 | test('ZodDiscriminatedUnionFaker.fake should be a function', () => { 30 | const schema = z.discriminatedUnion('type', [ 31 | z.object({ type: z.literal('a'), a: z.string() }), 32 | z.object({ type: z.literal('b'), b: z.string() }), 33 | ]) 34 | const faker = new ZodDiscriminatedUnionFaker(schema) 35 | expect(typeof faker.fake).toBe('function') 36 | }) 37 | 38 | test('ZodDiscriminatedUnionFaker.fake should return the given type', () => { 39 | const schema = z.discriminatedUnion('type', [ 40 | z.object({ type: z.literal('a'), a: z.string() }), 41 | z.object({ type: z.literal('b'), b: z.string() }), 42 | ]) 43 | const faker = new ZodDiscriminatedUnionFaker(schema) 44 | expectType, { type: 'a'; a: string } | { type: 'b'; b: string }>>(true) 45 | }) 46 | 47 | test('ZodDiscriminatedUnionFaker.fake should return a valid data', () => { 48 | install() 49 | 50 | const schema = z.discriminatedUnion('type', [ 51 | z.object({ type: z.literal('a'), a: z.string() }), 52 | z.object({ type: z.literal('b'), b: z.string() }), 53 | ]) 54 | const faker = new ZodDiscriminatedUnionFaker(schema) 55 | const data = faker.fake() 56 | expect(schema.safeParse(data).success).toBe(true) 57 | }) 58 | -------------------------------------------------------------------------------- /tests/v4/random.test.ts: -------------------------------------------------------------------------------- 1 | import { Faker } from '@faker-js/faker' 2 | import { describe, expect, it } from 'vitest' 3 | import * as z from 'zod' 4 | import { fake } from '../../src/v4' 5 | import { getFaker, randexp, seed, setFaker } from '../../src/v4/internals/random' 6 | 7 | describe('@faker-js/faker', () => { 8 | it('does not have a default faker', () => { 9 | expect(() => getFaker()).toThrow() 10 | }) 11 | 12 | it('can set a faker', () => { 13 | const faker = new Faker({ locale: {} }) 14 | 15 | setFaker(faker) 16 | 17 | expect(getFaker()).toEqual(faker) 18 | }) 19 | }) 20 | 21 | describe('randexp', () => { 22 | it('should works', () => { 23 | const regex = /^foo|bar$/ 24 | const data = randexp(regex) 25 | expect(data).toBeTypeOf('string') 26 | expect(data).toMatch(regex) 27 | }) 28 | }) 29 | 30 | describe('seed', () => { 31 | it('should set seed for getFaker', () => { 32 | const gen = () => getFaker().number.int() 33 | 34 | seed(97) 35 | const data1 = gen() 36 | expect(data1).toMatchInlineSnapshot(`7538522492335270`) 37 | 38 | seed(97) 39 | const data2 = gen() 40 | expect(data1).toBe(data2) 41 | 42 | const data3 = gen() 43 | expect(data1).not.toBe(data3) 44 | }) 45 | 46 | it('should set seed for randexp', () => { 47 | const gen = () => randexp(/\d{50}/) 48 | 49 | seed(61) 50 | const data1 = gen() 51 | expect(data1).toMatchInlineSnapshot(`"81849096331484486007704357152196940134457972127416"`) 52 | 53 | seed(61) 54 | const data2 = gen() 55 | expect(data1).toBe(data2) 56 | 57 | const data3 = gen() 58 | expect(data1).not.toBe(data3) 59 | }) 60 | 61 | it.skip('should works together', () => { 62 | const schema = z.object({ 63 | foo: z.number(), 64 | bar: z.number(), 65 | date: z.date(), 66 | string: z.object({ 67 | date: z.string().date(), 68 | datetime: z.string().datetime(), 69 | time: z.string().time(), 70 | }), 71 | }) 72 | const gen = () => fake(schema) 73 | 74 | seed(3) 75 | const data1 = gen() 76 | expect(data1).toMatchInlineSnapshot(` 77 | { 78 | "bar": 7138981630600537, 79 | "date": -271821-05-08T18:51:09.723Z, 80 | "foo": -3766725359666402, 81 | "string": { 82 | "date": "4408-02-05", 83 | "datetime": "0298-10-05T14:52:06.368Z", 84 | "time": "02:09:02", 85 | }, 86 | } 87 | `) 88 | 89 | seed(3) 90 | const data2 = gen() 91 | expect(data1).toEqual(data2) 92 | 93 | const data3 = gen() 94 | expect(data1).not.toEqual(data3) 95 | }) 96 | }) 97 | -------------------------------------------------------------------------------- /tests/v3/zod-set-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { describe, expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodSetFaker } from '../../src/v3/zod-set-faker' 6 | 7 | test('ZodSetFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodSetFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodSetFaker should accepts a ZodSet schema', () => { 13 | const schema = z.set(z.number()) 14 | expect(() => new ZodSetFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodSetFaker should return a ZodSetFaker instance', () => { 18 | const schema = z.set(z.number()) 19 | const faker = new ZodSetFaker(schema) 20 | expect(faker instanceof ZodSetFaker).toBe(true) 21 | }) 22 | 23 | test('ZodSetFaker.fake should be a function', () => { 24 | const schema = z.set(z.number()) 25 | const faker = new ZodSetFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | test('ZodSetFaker.fake should return set type', () => { 30 | const schema = z.set(z.number()) 31 | const faker = new ZodSetFaker(schema) 32 | expectType, Set>>(true) 33 | }) 34 | 35 | test('ZodSetFaker.fake should return a valid data', () => { 36 | install() 37 | 38 | const schema = z.set(z.number()) 39 | const faker = new ZodSetFaker(schema) 40 | const data = faker.fake() 41 | expect(schema.safeParse(data).success).toBe(true) 42 | }) 43 | 44 | test('non-empty', () => { 45 | install() 46 | 47 | const schema = z.set(z.number()).nonempty() 48 | const faker = new ZodSetFaker(schema) 49 | const data = faker.fake() 50 | expect(schema.safeParse(data).success).toBe(true) 51 | }) 52 | 53 | test('min', () => { 54 | install() 55 | 56 | const schema = z.set(z.number()).min(5) 57 | const faker = new ZodSetFaker(schema) 58 | const data = faker.fake() 59 | expect(schema.safeParse(data).success).toBe(true) 60 | }) 61 | 62 | test('max', () => { 63 | install() 64 | 65 | const schema = z.set(z.number()).max(5) 66 | const faker = new ZodSetFaker(schema) 67 | const data = faker.fake() 68 | expect(schema.safeParse(data).success).toBe(true) 69 | }) 70 | 71 | test('size', () => { 72 | install() 73 | 74 | const schema = z.set(z.number()).size(5) 75 | const faker = new ZodSetFaker(schema) 76 | const data = faker.fake() 77 | expect(schema.safeParse(data).success).toBe(true) 78 | }) 79 | 80 | describe('impossible case', () => { 81 | test('min > max', () => { 82 | install() 83 | 84 | const schema = z.set(z.number()).min(10).max(5) 85 | const faker = new ZodSetFaker(schema) 86 | expect(() => faker.fake()).toThrow(RangeError) 87 | }) 88 | }) 89 | -------------------------------------------------------------------------------- /tests/v3/zod-native-enum-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodNativeEnumFaker } from '../../src/v3/zod-native-enum-faker' 6 | 7 | enum NativeEnum { 8 | Foo, 9 | Bar, 10 | Baz = 'baz', 11 | Qux = 'qux', 12 | } 13 | 14 | test('ZodNativeEnumFaker should assert parameters', () => { 15 | const invalidSchema = void 0 as any 16 | expect(() => new ZodNativeEnumFaker(invalidSchema)).toThrow() 17 | }) 18 | 19 | test('ZodNativeEnumFaker should accepts a ZodNativeEnum schema', () => { 20 | const schema = z.nativeEnum(NativeEnum) 21 | expect(() => new ZodNativeEnumFaker(schema)).not.toThrow() 22 | }) 23 | 24 | test('ZodNativeEnumFaker should return a ZodNativeEnumFaker instance', () => { 25 | const schema = z.nativeEnum(NativeEnum) 26 | const faker = new ZodNativeEnumFaker(schema) 27 | expect(faker instanceof ZodNativeEnumFaker).toBe(true) 28 | }) 29 | 30 | test('ZodNativeEnumFaker.fake should be a function', () => { 31 | const schema = z.nativeEnum(NativeEnum) 32 | const faker = new ZodNativeEnumFaker(schema) 33 | expect(typeof faker.fake).toBe('function') 34 | }) 35 | 36 | test('ZodNativeEnumFaker.fake should return the give type', () => { 37 | const schema = z.nativeEnum(NativeEnum) 38 | const faker = new ZodNativeEnumFaker(schema) 39 | expectType, NativeEnum>>(false) 40 | }) 41 | 42 | test('ZodNativeEnumFaker.fake should return a valid data', () => { 43 | install() 44 | 45 | const schema = z.nativeEnum(NativeEnum) 46 | const faker = new ZodNativeEnumFaker(schema) 47 | const data = faker.fake() 48 | expect(schema.safeParse(data).success).toBe(true) 49 | }) 50 | 51 | test('numeric enums', () => { 52 | enum Fruits { 53 | Apple, 54 | Banana, 55 | } 56 | const schema = z.nativeEnum(Fruits) 57 | const faker = new ZodNativeEnumFaker(schema) 58 | const data = faker.fake() 59 | expect(schema.safeParse(data).success).toBe(true) 60 | }) 61 | 62 | test('string enums', () => { 63 | enum Fruits { 64 | Cantaloupe, // you can mix numerical and string enums 65 | Apple = 'apple', 66 | Banana = 'banana', 67 | } 68 | const schema = z.nativeEnum(Fruits) 69 | const faker = new ZodNativeEnumFaker(schema) 70 | const data = faker.fake() 71 | expect(schema.safeParse(data).success).toBe(true) 72 | }) 73 | 74 | test('const enums', () => { 75 | const Fruits = { 76 | Apple: 'apple', 77 | Banana: 'banana', 78 | Cantaloupe: 3, 79 | } as const 80 | 81 | const schema = z.nativeEnum(Fruits) 82 | const faker = new ZodNativeEnumFaker(schema) 83 | const data = faker.fake() 84 | expect(schema.safeParse(data).success).toBe(true) 85 | }) 86 | -------------------------------------------------------------------------------- /tests/v3/zod-effects-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { expect, test, vi } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodEffectsFaker } from '../../src/v3/zod-effects-faker' 6 | 7 | test('type', () => { 8 | install() 9 | const data = new ZodEffectsFaker(z.preprocess(val => String(val), z.string().min(5).max(10))).fake() 10 | expectType>(true) 11 | }) 12 | 13 | test('ZodEffectsFaker should return a ZodEffectsFaker instance', () => { 14 | const schema = z.preprocess(val => String(val), z.string().min(5).max(10)) 15 | const faker = new ZodEffectsFaker(schema) 16 | expect(faker instanceof ZodEffectsFaker).toBe(true) 17 | }) 18 | 19 | test('it should not throw error when schema has preprocess-effects', () => { 20 | install() 21 | const schema = z.string().min(5).max(10) 22 | const preprocess = z.preprocess(val => String(val), schema) 23 | const faker = new ZodEffectsFaker(preprocess) 24 | 25 | expect(() => faker.fake()).not.toThrow() 26 | 27 | const data = faker.fake() 28 | 29 | expect(schema.safeParse(data).success).toBe(true) 30 | }) 31 | 32 | test('it should ignore preprocess-effects', () => { 33 | install() 34 | const fn = vi.fn() 35 | const schema = z.string().min(5).max(10) 36 | const preprocess = z.preprocess(fn, schema) 37 | const faker = new ZodEffectsFaker(preprocess) 38 | 39 | faker.fake() 40 | 41 | expect(fn).not.toHaveBeenCalled() 42 | }) 43 | 44 | test('it should not throw error when schema has refine-effects', () => { 45 | install() 46 | const schema = z.string().refine(val => val.length <= 255, { 47 | message: "String can't be more than 255 characters", 48 | }) 49 | const faker = new ZodEffectsFaker(schema) 50 | 51 | const act = () => faker.fake() 52 | 53 | expect(act).not.toThrow() 54 | }) 55 | 56 | test('it should ignore refine-effects', () => { 57 | install() 58 | const fn = vi.fn() 59 | const schema = z.string().length(300).refine(fn, { 60 | message: "String can't be more than 255 characters", 61 | }) 62 | const faker = new ZodEffectsFaker(schema) 63 | 64 | faker.fake() 65 | 66 | expect(fn).not.toHaveBeenCalled() 67 | }) 68 | 69 | test('it should not throw error when schema has transform-effects', () => { 70 | install() 71 | const schema = z.string().transform(val => val.length) 72 | const faker = new ZodEffectsFaker(schema) 73 | 74 | const act = () => faker.fake() 75 | 76 | expect(act).not.toThrow() 77 | }) 78 | 79 | test('it should execute transform-effects', () => { 80 | install() 81 | const schema = z.string().transform(val => val.length) 82 | const faker = new ZodEffectsFaker(schema) 83 | 84 | const data = faker.fake() 85 | 86 | expectType>(true) 87 | expect(typeof data).toBe('number') 88 | }) 89 | -------------------------------------------------------------------------------- /tests/v3/zod-promise-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { afterEach, beforeEach, expect, test, vitest } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodPromiseFaker } from '../../src/v3/zod-promise-faker' 6 | 7 | beforeEach(() => { 8 | vitest.useFakeTimers() 9 | }) 10 | 11 | afterEach(() => { 12 | vitest.useRealTimers() 13 | }) 14 | 15 | test('ZodPromiseFaker should assert parameters', () => { 16 | const invalidSchema = void 0 as any 17 | expect(() => new ZodPromiseFaker(invalidSchema)).toThrow() 18 | }) 19 | 20 | test('ZodPromiseFaker should accepts a ZodPromise schema', () => { 21 | const schema = z.promise(z.string()) 22 | expect(() => new ZodPromiseFaker(schema)).not.toThrow() 23 | }) 24 | 25 | test('ZodPromiseFaker should return a ZodPromiseFaker instance', () => { 26 | const schema = z.promise(z.string()) 27 | const faker = new ZodPromiseFaker(schema) 28 | expect(faker instanceof ZodPromiseFaker).toBe(true) 29 | }) 30 | 31 | test('ZodPromiseFaker.fake should be a function', () => { 32 | const schema = z.promise(z.string()) 33 | const faker = new ZodPromiseFaker(schema) 34 | expect(typeof faker.fake).toBe('function') 35 | }) 36 | 37 | test('ZodPromiseFaker.fake should return promise type', () => { 38 | const schema = z.promise(z.string()) 39 | const faker = new ZodPromiseFaker(schema) 40 | expectType, Promise>>(true) 41 | }) 42 | 43 | test('ZodPromiseFaker.fake should return a valid data', async () => { 44 | install() 45 | 46 | const schema = z.promise(z.string()) 47 | const faker = new ZodPromiseFaker(schema) 48 | const data = faker.fake() 49 | vitest.runAllTimers() 50 | expect((await schema.safeParseAsync(data)).success).toBe(true) 51 | }) 52 | 53 | test('microtask', async () => { 54 | install() 55 | const schema = z.promise(z.string()) 56 | const faker = new ZodPromiseFaker(schema) 57 | 58 | while (true) { 59 | let data 60 | faker.fake().then(_data => { 61 | data = _data 62 | }) 63 | const micro = Promise.resolve() 64 | vitest.runAllTicks() 65 | await micro 66 | if (typeof data === 'string') { 67 | return 68 | } 69 | } 70 | }) 71 | 72 | test('task', async () => { 73 | install() 74 | const schema = z.promise(z.string()) 75 | const faker = new ZodPromiseFaker(schema) 76 | 77 | while (true) { 78 | let data 79 | faker.fake().then(_data => { 80 | data = _data 81 | }) 82 | const micro = Promise.resolve() 83 | const macro = new Promise(resolve => setTimeout(resolve)) 84 | vitest.runAllTicks() 85 | await micro 86 | if (typeof data === 'string') { 87 | continue 88 | } 89 | await vitest.advanceTimersByTime(0) 90 | await macro 91 | if (typeof data === 'string') { 92 | return 93 | } 94 | } 95 | }) 96 | -------------------------------------------------------------------------------- /tests/v3/integration.test.ts: -------------------------------------------------------------------------------- 1 | import { beforeEach, expect } from 'vitest' 2 | import { z } from 'zod/v3' 3 | import { fake, install } from '../../src/v3' 4 | import { testMultipleTimes } from './util' 5 | 6 | beforeEach(() => { 7 | install() 8 | }) 9 | 10 | testMultipleTimes('integration', async () => { 11 | interface Category { 12 | name: string 13 | subcategories: Category[] 14 | } 15 | const category = z.lazy(() => 16 | z.object({ 17 | name: z.string(), 18 | subcategories: z.array(category), 19 | }), 20 | ) as z.ZodType 21 | 22 | const schema = z.object({ 23 | primitives: z.object({ 24 | string: z.string(), 25 | number: z.number(), 26 | bigint: z.bigint(), 27 | boolean: z.boolean(), 28 | date: z.date(), 29 | }), 30 | emptyValues: z.object({ 31 | undefined: z.undefined(), 32 | null: z.null(), 33 | void: z.void(), 34 | }), 35 | any: z.any(), 36 | unknown: z.unknown(), 37 | // never: z.never(), // always throws an error 38 | literal: z.literal('tuna'), 39 | strings: z.object({ 40 | max: z.string().max(5), 41 | min: z.string().min(5), 42 | length: z.string().length(5), 43 | email: z.string().email(), 44 | url: z.string().url(), 45 | uuid: z.string().uuid(), 46 | cuid: z.string().cuid(), 47 | regex: z.string().regex(/hello+ (world|to you)/), 48 | }), 49 | numbers: z.object({ 50 | gt: z.number().gt(5), 51 | gte: z.number().gte(5), 52 | lt: z.number().lt(5), 53 | lte: z.number().lte(5), 54 | int: z.number().int(), 55 | positive: z.number().positive(), 56 | nonnegative: z.number().nonnegative(), 57 | negative: z.number().negative(), 58 | nonpositive: z.number().nonpositive(), 59 | multipleOf: z.number().multipleOf(31), 60 | }), 61 | nan: z.nan(), 62 | boolean: z.boolean(), 63 | date: z.date(), 64 | enum: z.enum(['Salmon', 'Tuna', 'Trout']), 65 | nativeEnum: z.nativeEnum({ 66 | Apple: 'apple', 67 | Banana: 'banana', 68 | Cantaloupe: 3, 69 | } as const), 70 | optional: z.optional(z.string()), 71 | nullable: z.nullable(z.string()), 72 | object: z.object({ 73 | name: z.string(), 74 | age: z.number(), 75 | }), 76 | array: z.array(z.string()), 77 | tuple: z.tuple([ 78 | z.string(), 79 | z.number(), 80 | z.object({ 81 | pointsScored: z.number(), 82 | }), 83 | ]), 84 | union: z.union([z.string(), z.number()]), 85 | discriminatedUnions: z.discriminatedUnion('type', [ 86 | z.object({ type: z.literal('a'), a: z.string() }), 87 | z.object({ type: z.literal('b'), b: z.string() }), 88 | ]), 89 | record: z.record(z.string(), z.number()), 90 | map: z.map(z.string(), z.number()), 91 | set: z.set(z.number()), 92 | lazy: category, 93 | promise: z.promise(z.number()), 94 | }) 95 | 96 | const data = fake(schema) 97 | 98 | expect(schema.safeParse(data).success).toBe(true) 99 | 100 | await data.promise 101 | }) 102 | -------------------------------------------------------------------------------- /tests/v3/random.test.ts: -------------------------------------------------------------------------------- 1 | import { fakerAR, fakerEN, fakerJA } from '@faker-js/faker' 2 | import { describe, expect, test, vi } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { fake, getFaker, install, installFaker, randexp, runFake, seed, setFaker } from '../../src/v3' 5 | 6 | describe('@faker-js/faker', () => { 7 | test('default', () => { 8 | const spy = vi.spyOn(fakerEN.lorem, 'word') 9 | 10 | const data = getFaker().lorem.word() 11 | expect(data).toBeTypeOf('string') 12 | expect(spy).toHaveBeenCalled() 13 | 14 | spy.mockReset() 15 | 16 | const data2 = runFake(faker => faker.lorem.word()) 17 | expect(data2).toBeTypeOf('string') 18 | expect(spy).toHaveBeenCalled() 19 | }) 20 | 21 | test('custom', () => { 22 | const spyJA = vi.spyOn(fakerJA.lorem, 'word') 23 | setFaker(fakerJA) 24 | const data = getFaker().lorem.word() 25 | expect(data).toBeTypeOf('string') 26 | expect(spyJA).toHaveBeenCalled() 27 | 28 | const spyAR = vi.spyOn(fakerAR.lorem, 'word') 29 | installFaker(fakerAR) 30 | const data2 = runFake(faker => faker.lorem.word()) 31 | expect(data2).toBeTypeOf('string') 32 | expect(spyAR).toHaveBeenCalled() 33 | }) 34 | 35 | test('runFake can not be used with async functions', () => { 36 | // @ts-expect-error 37 | expect(() => runFake(async () => {})).toThrow() 38 | }) 39 | }) 40 | 41 | describe('randexp', () => { 42 | test('randexp', () => { 43 | const regex = /^foo|bar$/ 44 | const data = randexp(regex) 45 | expect(data).toBeTypeOf('string') 46 | expect(data).toMatch(regex) 47 | }) 48 | }) 49 | 50 | describe('seed', () => { 51 | test('getFaker', () => { 52 | const gen = () => getFaker().number.int() 53 | 54 | seed(97) 55 | const data1 = gen() 56 | expect(data1).toMatchInlineSnapshot(`7538522492335270`) 57 | 58 | seed(97) 59 | const data2 = gen() 60 | expect(data1).toBe(data2) 61 | 62 | const data3 = gen() 63 | expect(data1).not.toBe(data3) 64 | }) 65 | 66 | test('randexp', () => { 67 | const gen = () => randexp(/\d{50}/) 68 | 69 | seed(61) 70 | const data1 = gen() 71 | expect(data1).toMatchInlineSnapshot(`"81849096331484486007704357152196940134457972127416"`) 72 | 73 | seed(61) 74 | const data2 = gen() 75 | expect(data1).toBe(data2) 76 | 77 | const data3 = gen() 78 | expect(data1).not.toBe(data3) 79 | }) 80 | 81 | test('integration', () => { 82 | install() 83 | const schema = z.object({ 84 | foo: z.number(), 85 | bar: z.number(), 86 | date: z.date(), 87 | string: z.object({ 88 | date: z.string().date(), 89 | datetime: z.string().datetime(), 90 | time: z.string().time(), 91 | }), 92 | }) 93 | const gen = () => fake(schema) 94 | 95 | seed(3) 96 | const data1 = gen() 97 | expect(data1).toMatchInlineSnapshot(` 98 | { 99 | "bar": 7138981630600537, 100 | "date": -271821-05-08T18:51:09.723Z, 101 | "foo": -3766725359666402, 102 | "string": { 103 | "date": "4408-02-05", 104 | "datetime": "0298-10-05T14:52:06.368Z", 105 | "time": "02:09:02", 106 | }, 107 | } 108 | `) 109 | 110 | seed(3) 111 | const data2 = gen() 112 | expect(data1).toEqual(data2) 113 | 114 | const data3 = gen() 115 | expect(data1).not.toEqual(data3) 116 | }) 117 | }) 118 | -------------------------------------------------------------------------------- /tests/v3/zod-date-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { describe, expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { ZodDateFaker } from '../../src/v3/zod-date-faker' 5 | 6 | test('ZodDateFaker should assert parameters', () => { 7 | const invalidSchema = void 0 as any 8 | expect(() => new ZodDateFaker(invalidSchema)).toThrow() 9 | }) 10 | 11 | test('ZodDateFaker should accepts a ZodDate schema', () => { 12 | const schema = z.date() 13 | expect(() => new ZodDateFaker(schema)).not.toThrow() 14 | }) 15 | 16 | test('ZodDateFaker should return a ZodDateFaker instance', () => { 17 | const schema = z.date() 18 | const faker = new ZodDateFaker(schema) 19 | expect(faker instanceof ZodDateFaker).toBe(true) 20 | }) 21 | 22 | test('ZodDateFaker.fake should be a function', () => { 23 | const schema = z.date() 24 | const faker = new ZodDateFaker(schema) 25 | expect(typeof faker.fake).toBe('function') 26 | }) 27 | 28 | test('ZodDateFaker.fake should return date type', () => { 29 | const schema = z.date() 30 | const faker = new ZodDateFaker(schema) 31 | expectType, Date>>(true) 32 | }) 33 | 34 | test('ZodDateFaker.fake should return a valid data', () => { 35 | const schema = z.date() 36 | const faker = new ZodDateFaker(schema) 37 | const data = faker.fake() 38 | expect(schema.safeParse(data).success).toBe(true) 39 | }) 40 | 41 | test('min', () => { 42 | const schema = z.date().min(new Date('2000-01-01')) 43 | const faker = new ZodDateFaker(schema) 44 | const data = faker.fake() 45 | expect(schema.safeParse(data).success).toBe(true) 46 | }) 47 | 48 | test('max', () => { 49 | const schema = z.date().max(new Date('2000-01-01')) 50 | const faker = new ZodDateFaker(schema) 51 | const data = faker.fake() 52 | expect(schema.safeParse(data).success).toBe(true) 53 | }) 54 | 55 | test('should sometimes generate an edge date (min)', () => { 56 | const schema = z.date() 57 | const faker = new ZodDateFaker(schema) 58 | while (true) { 59 | const data = faker.fake() 60 | if (data.getTime() < new Date('1970-01-01').getTime()) { 61 | break 62 | } 63 | } 64 | }) 65 | 66 | test('should sometimes generate an edge date (max)', () => { 67 | const schema = z.date() 68 | const faker = new ZodDateFaker(schema) 69 | while (true) { 70 | const data = faker.fake() 71 | if (data.getTime() > new Date('2038-01-19').getTime()) { 72 | break 73 | } 74 | } 75 | }) 76 | 77 | describe('multiple checks of the same kind', () => { 78 | test('min', () => { 79 | const schema = z 80 | .date() 81 | .min(new Date('2000-01-01T00:00:00.000Z')) 82 | .min(new Date('2002-01-01T00:00:00.000Z')) 83 | .min(new Date('2001-01-01T00:00:00.000Z')) 84 | .max(new Date('2002-01-01T00:00:00.000Z')) 85 | const faker = new ZodDateFaker(schema) 86 | const data = faker.fake() 87 | expect(schema.safeParse(data).data).toEqual(new Date('2002-01-01T00:00:00.000Z')) 88 | }) 89 | 90 | test('max', () => { 91 | const schema = z 92 | .date() 93 | .max(new Date('2002-01-01T00:00:00.000Z')) 94 | .max(new Date('2000-01-01T00:00:00.000Z')) 95 | .max(new Date('2001-01-01T00:00:00.000Z')) 96 | .min(new Date('2000-01-01T00:00:00.000Z')) 97 | const faker = new ZodDateFaker(schema) 98 | const data = faker.fake() 99 | expect(schema.safeParse(data).data).toEqual(new Date('2000-01-01T00:00:00.000Z')) 100 | }) 101 | }) 102 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Open Development 4 | 5 | All work on Zod-schema-faker happens directly on GitHub. 6 | 7 | ## Semantic Versioning 8 | 9 | Zod-schema-faker follows [semantic versioning](https://semver.org/). We release patch versions for critical bugfixes, 10 | minor versions for new features or non-essential changes, and major versions for any breaking changes. When we make 11 | breaking changes, we also introduce deprecation warnings in a minor version so that our users learn about the upcoming 12 | changes and migrate their code in advance. 13 | 14 | Every significant change is documented in the 15 | [changelog file](https://github.com/soc221b/zod-schema-faker/blob/main/CHANGELOG.md). 16 | 17 | ## Branch Organization 18 | 19 | Submit all changes directly to the [main branch](https://github.com/soc221b/zod-schema-faker/tree/main). We don’t use 20 | separate branches for development or for upcoming releases. We do our best to keep `main` in good shape, with all tests 21 | passing. 22 | 23 | Code that lands in `main` must be compatible with the latest stable release. It may contain additional features, but no 24 | breaking changes. We should be able to release a new minor version from the tip of `main` at any time. 25 | 26 | ## Bugs 27 | 28 | ### Where to Find Known Issues 29 | 30 | We are using [GitHub Issues](https://github.com/soc221b/zod-schema-faker/issues) for our public bugs. We keep a close 31 | eye on this and try to make it clear when we have an internal fix in progress. Before filing a new task, try to make 32 | sure your problem doesn’t already exist. 33 | 34 | ### Reporting New Issues 35 | 36 | The best way to get your bug fixed is to provide a reduced test case. This 37 | [StackBlitz template](https://stackblitz.com/edit/zod-schema-faker?file=src%2Fmain.ts) is a great starting point. 38 | 39 | ## Proposing a Change 40 | 41 | If you intend to change the public API, or make any non-trivial changes to the implementation, we recommend 42 | [filing an issue](https://github.com/soc221b/zod-schema-faker/issues/new). This lets us reach an agreement on your 43 | proposal before you put significant effort into it. 44 | 45 | If you’re only fixing a bug, it’s fine to submit a pull request right away but we still recommend to file an issue 46 | detailing what you’re fixing. This is helpful in case we don’t accept that specific fix but want to keep track of the 47 | issue. 48 | 49 | ## Sending a Pull Request 50 | 51 | The core team is monitoring for pull requests. We will review your pull request and either merge it, request changes to 52 | it, or close it with an explanation. We’ll do our best to provide updates and feedback throughout the process. 53 | 54 | **Before submitting a pull request, please make sure the following is done:** 55 | 56 | 1. Fork [the repository](https://github.com/soc221b/zod-schema-faker) and create your branch from `main`. 57 | 2. Run `npm ci` in the repository root. 58 | 3. If you’ve fixed a bug or added code that should be tested, add tests! 59 | 4. Ensure the test suite passes (`npm run test`). 60 | 5. Format your code (`npm run format`). 61 | 62 | ## Contribution Prerequisites 63 | 64 | - You have Node installed at LTS. 65 | - You are familiar with Git. 66 | 67 | ## Development Workflow 68 | 69 | After cloning Zod-schema-faker, run `npm ci` to fetch its dependencies. Then, you can run several commands: 70 | 71 | - `npm run build` builds the library. 72 | - `npm run format` checks the code style. 73 | - `npm run test` runs the test suite. 74 | 75 | ## Style Guide 76 | 77 | We use an automatic code formatter called [Prettier](https://prettier.io/). Run `npm run format` after making any 78 | changes to the code. 79 | 80 | ## License 81 | 82 | By contributing to Zod-schema-faker, you agree that your contributions will be licensed under its MIT license. 83 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/number.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | import { lcm } from '../utils' 7 | 8 | export function fakeNumber( 9 | schema: T, 10 | context: Context, 11 | rootFake: typeof internalFake, 12 | ): Infer { 13 | let min = Number.MIN_SAFE_INTEGER 14 | let max = Number.MAX_SAFE_INTEGER 15 | let multipleOf = undefined 16 | let int = false 17 | const format = (schema as unknown as core.$ZodNumberFormat)._zod.def.format 18 | switch (format) { 19 | case 'float32': { 20 | int = false 21 | break 22 | } 23 | case 'float64': { 24 | int = false 25 | break 26 | } 27 | case 'int32': { 28 | int = true 29 | min = Math.max(min, -2147483647) 30 | max = Math.min(max, 2147483646) 31 | break 32 | } 33 | case 'safeint': { 34 | int = true 35 | break 36 | } 37 | case 'uint32': { 38 | int = true 39 | min = Math.max(min, 0) 40 | max = Math.min(max, 4294967295) 41 | break 42 | } 43 | default: { 44 | const _: never = format 45 | break 46 | } 47 | } 48 | for (const check of (schema._zod.def.checks ?? []) as core.$ZodChecks[]) { 49 | switch (check._zod.def.check) { 50 | case 'greater_than': { 51 | const _min = Number(check._zod.def.value) + (check._zod.def.inclusive ? 0 : 0.000000000000001) 52 | min = min !== undefined ? Math.max(min, _min) : _min 53 | break 54 | } 55 | case 'less_than': { 56 | const _max = Number(check._zod.def.value) - (check._zod.def.inclusive ? 0 : 0.000000000000001) 57 | max = max !== undefined ? Math.min(max, _max) : _max 58 | break 59 | } 60 | case 'multiple_of': { 61 | const _multipleOf = Number(check._zod.def.value) 62 | multipleOf = multipleOf !== undefined ? lcm(multipleOf, _multipleOf) : _multipleOf 63 | break 64 | } 65 | case 'number_format': { 66 | const format = (check as unknown as core.$ZodNumberFormat)._zod.def.format 67 | switch (format) { 68 | case 'float32': { 69 | int = false 70 | break 71 | } 72 | case 'float64': { 73 | int = false 74 | break 75 | } 76 | case 'int32': { 77 | int = true 78 | min = Math.max(min, -2147483647) 79 | max = Math.min(max, 2147483646) 80 | break 81 | } 82 | case 'safeint': { 83 | int = true 84 | break 85 | } 86 | case 'uint32': { 87 | int = true 88 | min = Math.max(min, 0) 89 | max = Math.min(max, 4294967295) 90 | break 91 | } 92 | default: { 93 | const _: never = format 94 | break 95 | } 96 | } 97 | break 98 | } 99 | default: { 100 | const _: 101 | | 'bigint_format' 102 | | 'length_equals' 103 | | 'max_length' 104 | | 'max_size' 105 | | 'mime_type' 106 | | 'min_length' 107 | | 'min_size' 108 | | 'overwrite' 109 | | 'property' 110 | | 'size_equals' 111 | | 'string_format' 112 | | never = check._zod.def.check 113 | break 114 | } 115 | } 116 | } 117 | if (multipleOf !== undefined && multipleOf === parseInt(multipleOf.toString(), 10)) { 118 | int = true 119 | } 120 | if (int) { 121 | return getFaker().number.int({ min, max, multipleOf }) 122 | } else { 123 | return getFaker().number.float({ min, max, multipleOf }) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /tests/v3/zod-array-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { describe, expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodArrayFaker } from '../../src/v3/zod-array-faker' 6 | 7 | test('ZodArrayFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodArrayFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodArrayFaker should accepts a ZodArray schema', () => { 13 | const schema = z.array(z.number()) 14 | expect(() => new ZodArrayFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodArrayFaker should return a ZodArrayFaker instance', () => { 18 | const schema = z.array(z.number()) 19 | const faker = new ZodArrayFaker(schema) 20 | expect(faker instanceof ZodArrayFaker).toBe(true) 21 | }) 22 | 23 | test('ZodArrayFaker.fake should be a function', () => { 24 | const schema = z.array(z.number()) 25 | const faker = new ZodArrayFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | test('ZodArrayFaker.fake should return array type', () => { 30 | const schema = z.array(z.number()) 31 | const faker = new ZodArrayFaker(schema) 32 | expectType, number[]>>(true) 33 | }) 34 | 35 | test('ZodArrayFaker.fake should return a valid data', () => { 36 | install() 37 | 38 | const schema = z.array(z.number()) 39 | const faker = new ZodArrayFaker(schema) 40 | const data = faker.fake() 41 | expect(schema.safeParse(data).success).toBe(true) 42 | }) 43 | 44 | test('non-empty', () => { 45 | install() 46 | 47 | const schema = z.array(z.number()).nonempty() 48 | const faker = new ZodArrayFaker(schema) 49 | const data = faker.fake() 50 | expect(schema.safeParse(data).success).toBe(true) 51 | }) 52 | 53 | test('min', () => { 54 | install() 55 | 56 | const schema = z.array(z.number()).min(5) 57 | const faker = new ZodArrayFaker(schema) 58 | const data = faker.fake() 59 | expect(schema.safeParse(data).success).toBe(true) 60 | }) 61 | 62 | test('max', () => { 63 | install() 64 | 65 | const schema = z.array(z.number()).max(5) 66 | const faker = new ZodArrayFaker(schema) 67 | const data = faker.fake() 68 | expect(schema.safeParse(data).success).toBe(true) 69 | }) 70 | 71 | test('length', () => { 72 | install() 73 | 74 | const schema = z.array(z.number()).length(5) 75 | const faker = new ZodArrayFaker(schema) 76 | const data = faker.fake() 77 | expect(schema.safeParse(data).success).toBe(true) 78 | }) 79 | 80 | describe('multiple checks of the same kind', () => { 81 | test('min', () => { 82 | install() 83 | 84 | const schema = z.array(z.number()).min(5).min(3).min(4).max(5) 85 | const faker = new ZodArrayFaker(schema) 86 | const data = faker.fake() 87 | expect(schema.safeParse(data).data?.length).toBeGreaterThanOrEqual(4) 88 | }) 89 | 90 | test('max', () => { 91 | install() 92 | 93 | const schema = z.array(z.number()).max(3).max(5).max(4).min(3) 94 | const faker = new ZodArrayFaker(schema) 95 | const data = faker.fake() 96 | expect(schema.safeParse(data).data?.length).toBeLessThanOrEqual(4) 97 | }) 98 | }) 99 | 100 | describe('impossible case', () => { 101 | test('min > max', () => { 102 | const schema = z.array(z.number()).min(5).max(4) 103 | const faker = new ZodArrayFaker(schema) 104 | expect(() => faker.fake()).toThrow(RangeError) 105 | }) 106 | 107 | test('min !== length', () => { 108 | const schema = z.array(z.number()).min(5).length(4) 109 | const faker = new ZodArrayFaker(schema) 110 | expect(() => faker.fake()).toThrow(RangeError) 111 | }) 112 | 113 | test('max !== length', () => { 114 | const schema = z.array(z.number()).max(5).length(4) 115 | const faker = new ZodArrayFaker(schema) 116 | expect(() => faker.fake()).toThrow(RangeError) 117 | }) 118 | }) 119 | -------------------------------------------------------------------------------- /tests/v3/zod-object-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { describe, expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { install } from '../../src/v3' 5 | import { ZodObjectFaker } from '../../src/v3/zod-object-faker' 6 | 7 | test('ZodObjectFaker should assert parameters', () => { 8 | const invalidSchema = void 0 as any 9 | expect(() => new ZodObjectFaker(invalidSchema)).toThrow() 10 | }) 11 | 12 | test('ZodObjectFaker should accepts a ZodObject schema', () => { 13 | const schema = z.object({ foo: z.number(), bar: z.string() }) 14 | expect(() => new ZodObjectFaker(schema)).not.toThrow() 15 | }) 16 | 17 | test('ZodObjectFaker should return a ZodObjectFaker instance', () => { 18 | const schema = z.object({ foo: z.number(), bar: z.string() }) 19 | const faker = new ZodObjectFaker(schema) 20 | expect(faker instanceof ZodObjectFaker).toBe(true) 21 | }) 22 | 23 | test('ZodObjectFaker.fake should be a function', () => { 24 | const schema = z.object({ foo: z.number(), bar: z.string() }) 25 | const faker = new ZodObjectFaker(schema) 26 | expect(typeof faker.fake).toBe('function') 27 | }) 28 | 29 | describe('default', () => { 30 | test('ZodObjectFaker.fake should return object type', () => { 31 | const schema = z.object({ foo: z.number(), bar: z.string() }) 32 | const faker = new ZodObjectFaker(schema) 33 | expectType, { foo: number; bar: string }>>(true) 34 | }) 35 | 36 | test('ZodObjectFaker.fake should return a valid data', () => { 37 | install() 38 | 39 | const schema = z.object({ foo: z.number(), bar: z.string() }) 40 | const faker = new ZodObjectFaker(schema) 41 | const data = faker.fake() 42 | expect(schema.safeParse(data).success).toBe(true) 43 | }) 44 | }) 45 | 46 | describe('passthrough', () => { 47 | test('ZodObjectFaker.fake should return object type', () => { 48 | const schema = z.object({ foo: z.number(), bar: z.string() }).passthrough() 49 | const faker = new ZodObjectFaker(schema) 50 | expectType, { foo: number; bar: string } & { [k: string]: unknown }>>(true) 51 | }) 52 | 53 | test('ZodObjectFaker.fake should return a valid data', () => { 54 | install() 55 | 56 | const schema = z.object({ foo: z.number(), bar: z.string() }).passthrough() 57 | const faker = new ZodObjectFaker(schema) 58 | const data = faker.fake() 59 | expect(schema.safeParse(data).success).toBe(true) 60 | }) 61 | 62 | test('ZodObjectFaker.fake should sometimes return extra keys', () => { 63 | install() 64 | 65 | const schema = z.object({ foo: z.number(), bar: z.string() }).passthrough() 66 | const faker = new ZodObjectFaker(schema) 67 | while (true) { 68 | const data = faker.fake() 69 | if (Object.keys(data).length > 2) { 70 | return 71 | } 72 | } 73 | }) 74 | }) 75 | 76 | describe('strict', () => { 77 | test('ZodObjectFaker.fake should return object type', () => { 78 | const schema = z.object({ foo: z.number(), bar: z.string() }).strict() 79 | const faker = new ZodObjectFaker(schema) 80 | expectType, { foo: number; bar: string }>>(true) 81 | }) 82 | 83 | test('ZodObjectFaker.fake should return a valid data', () => { 84 | install() 85 | 86 | const schema = z.object({ foo: z.number(), bar: z.string() }).strict() 87 | const faker = new ZodObjectFaker(schema) 88 | const data = faker.fake() 89 | expect(schema.safeParse(data).success).toBe(true) 90 | }) 91 | }) 92 | 93 | describe('catchall', () => { 94 | test('ZodObjectFaker.fake should return object type', () => { 95 | const schema = z.object({ foo: z.number(), bar: z.string() }).catchall(z.boolean()) 96 | const faker = new ZodObjectFaker(schema) 97 | expectType< 98 | TypeEqual< 99 | ReturnType, 100 | { foo: number; bar: string } & { 101 | [k: string]: boolean 102 | } 103 | > 104 | >(true) 105 | }) 106 | 107 | test('ZodObjectFaker.fake should return a valid data', () => { 108 | install() 109 | 110 | const schema = z.object({ foo: z.number(), bar: z.string() }).catchall(z.boolean()) 111 | const faker = new ZodObjectFaker(schema) 112 | const data = faker.fake() 113 | expect(schema.safeParse(data).success).toBe(true) 114 | }) 115 | }) 116 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/string.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { Context } from '../context' 3 | import { rootFake as internalFake } from '../fake' 4 | import { getFaker } from '../random' 5 | import { Infer } from '../type' 6 | import { unescape } from '../utils' 7 | import { fakeStringFormat } from './checks/string-format' 8 | 9 | export function fakeString( 10 | schema: T, 11 | context: Context, 12 | rootFake: typeof internalFake, 13 | ): Infer { 14 | let data = getFaker().lorem.sentence() 15 | let min = undefined 16 | let max = undefined 17 | let startsWith = '' 18 | let includes = '' 19 | let endsWith = '' 20 | let uppercase = false 21 | let lowercase = false 22 | data = fakeStringFormat(schema as any, rootFake) ?? data 23 | for (const check of (schema._zod.def.checks ?? []) as core.$ZodChecks[]) { 24 | switch (check._zod.def.check) { 25 | case 'length_equals': { 26 | min = check._zod.def.length 27 | max = check._zod.def.length 28 | break 29 | } 30 | case 'max_length': { 31 | max = Math.min(max ?? check._zod.def.maximum, check._zod.def.maximum) 32 | break 33 | } 34 | case 'min_length': { 35 | min = Math.max(min ?? check._zod.def.minimum, check._zod.def.minimum) 36 | break 37 | } 38 | case 'string_format': { 39 | data = fakeStringFormat(check as any, rootFake) ?? data 40 | const checkStringFormat = check as core.$ZodCheckStringFormat 41 | const format = checkStringFormat._zod.def.format as core.$ZodStringFormats 42 | switch (format) { 43 | case 'ends_with': { 44 | endsWith = unescape(checkStringFormat._zod.def.pattern?.source ?? '') 45 | .replace(/^\.\*/, '') 46 | .replace(/\$$/, '') 47 | break 48 | } 49 | case 'includes': { 50 | includes = unescape(checkStringFormat._zod.def.pattern?.source ?? '') 51 | break 52 | } 53 | case 'lowercase': { 54 | lowercase = true 55 | break 56 | } 57 | case 'starts_with': { 58 | startsWith = unescape(checkStringFormat._zod.def.pattern?.source ?? '') 59 | .replace(/^\^/, '') 60 | .replace(/\.\*$/, '') 61 | break 62 | } 63 | case 'uppercase': { 64 | uppercase = true 65 | break 66 | } 67 | default: { 68 | const _: 69 | | 'base64' 70 | | 'base64url' 71 | | 'cidrv4' 72 | | 'cidrv6' 73 | | 'cuid' 74 | | 'cuid2' 75 | | 'date' 76 | | 'datetime' 77 | | 'duration' 78 | | 'e164' 79 | | 'email' 80 | | 'emoji' 81 | | 'guid' 82 | | 'ipv4' 83 | | 'ipv6' 84 | | 'json_string' 85 | | 'jwt' 86 | | 'ksuid' 87 | | 'nanoid' 88 | | 'regex' 89 | | 'time' 90 | | 'ulid' 91 | | 'url' 92 | | 'uuid' 93 | | 'xid' 94 | | never = format 95 | break 96 | } 97 | } 98 | break 99 | } 100 | default: { 101 | const _: 102 | | 'bigint_format' 103 | | 'greater_than' 104 | | 'less_than' 105 | | 'max_size' 106 | | 'mime_type' 107 | | 'min_size' 108 | | 'multiple_of' 109 | | 'number_format' 110 | | 'overwrite' 111 | | 'property' 112 | | 'size_equals' 113 | | never = check._zod.def.check 114 | break 115 | } 116 | } 117 | } 118 | max = max === Infinity ? min : max 119 | 120 | if (min !== undefined && data.length < min) { 121 | data = data.padEnd(min, ' ') 122 | } 123 | if (max !== undefined && data.length > max) { 124 | data = data.slice(0, getFaker().number.int({ min, max })) 125 | } 126 | if (uppercase) { 127 | data = data.toUpperCase() 128 | } 129 | if (lowercase) { 130 | data = data.toLowerCase() 131 | } 132 | if (startsWith && !data.startsWith(startsWith)) { 133 | data = startsWith + data 134 | } 135 | if (includes && !data.includes(includes)) { 136 | data = data + includes 137 | } 138 | if (endsWith && !data.endsWith(endsWith)) { 139 | data = data + endsWith 140 | } 141 | return data 142 | } 143 | -------------------------------------------------------------------------------- /e2e/issue-189/v4/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "issue-189", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "issue-189", 8 | "dependencies": { 9 | "zod": "4.1.9" 10 | }, 11 | "devDependencies": { 12 | "@faker-js/faker": "10.1.0", 13 | "@types/node": "24.10.4", 14 | "ts-expect": "1.3.0", 15 | "typescript": "5.9.3" 16 | } 17 | }, 18 | "../..": { 19 | "version": "2.0.0-beta.8", 20 | "extraneous": true, 21 | "license": "MIT", 22 | "dependencies": { 23 | "@faker-js/faker": "10.0.0", 24 | "randexp": "0.5.3" 25 | }, 26 | "devDependencies": { 27 | "@types/node": "22.18.8", 28 | "@vitest/coverage-v8": "3.2.4", 29 | "prettier": "3.6.2", 30 | "prettier-plugin-multiline-arrays": "4.0.3", 31 | "prettier-plugin-organize-imports": "4.3.0", 32 | "prettier-plugin-packagejson": "2.5.19", 33 | "prettier-plugin-sort-json": "4.1.1", 34 | "rimraf": "6.0.1", 35 | "ts-expect": "1.3.0", 36 | "ts-node": "10.9.2", 37 | "typescript": "5.9.3", 38 | "vite": "7.1.7", 39 | "vite-plugin-dts": "4.5.4", 40 | "vitest": "3.2.4", 41 | "zod": "4.1.9" 42 | } 43 | }, 44 | "../../..": { 45 | "version": "2.0.0-beta.8", 46 | "extraneous": true, 47 | "license": "MIT", 48 | "dependencies": { 49 | "@faker-js/faker": "10.1.0", 50 | "randexp": "0.5.3" 51 | }, 52 | "devDependencies": { 53 | "@types/node": "24.10.1", 54 | "@vitest/coverage-v8": "4.0.9", 55 | "prettier": "3.6.2", 56 | "prettier-plugin-multiline-arrays": "4.0.3", 57 | "prettier-plugin-organize-imports": "4.3.0", 58 | "prettier-plugin-packagejson": "2.5.19", 59 | "prettier-plugin-sort-json": "4.1.1", 60 | "rimraf": "6.1.0", 61 | "ts-expect": "1.3.0", 62 | "ts-node": "10.9.2", 63 | "typescript": "5.9.3", 64 | "vite": "7.2.2", 65 | "vite-plugin-dts": "4.5.4", 66 | "vitest": "4.0.9", 67 | "zod": "4.1.12" 68 | }, 69 | "peerDependencies": { 70 | "zod": "^3.25.0 || ^4.0.0" 71 | } 72 | }, 73 | "node_modules/@faker-js/faker": { 74 | "version": "10.1.0", 75 | "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-10.1.0.tgz", 76 | "integrity": "sha512-C3mrr3b5dRVlKPJdfrAXS8+dq+rq8Qm5SNRazca0JKgw1HQERFmrVb0towvMmw5uu8hHKNiQasMaR/tydf3Zsg==", 77 | "dev": true, 78 | "funding": [ 79 | { 80 | "type": "opencollective", 81 | "url": "https://opencollective.com/fakerjs" 82 | } 83 | ], 84 | "license": "MIT", 85 | "engines": { 86 | "node": "^20.19.0 || ^22.13.0 || ^23.5.0 || >=24.0.0", 87 | "npm": ">=10" 88 | } 89 | }, 90 | "node_modules/@types/node": { 91 | "version": "24.10.4", 92 | "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", 93 | "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", 94 | "dev": true, 95 | "license": "MIT", 96 | "dependencies": { 97 | "undici-types": "~7.16.0" 98 | } 99 | }, 100 | "node_modules/ts-expect": { 101 | "version": "1.3.0", 102 | "resolved": "https://registry.npmjs.org/ts-expect/-/ts-expect-1.3.0.tgz", 103 | "integrity": "sha512-e4g0EJtAjk64xgnFPD6kTBUtpnMVzDrMb12N1YZV0VvSlhnVT3SGxiYTLdGy8Q5cYHOIC/FAHmZ10eGrAguicQ==", 104 | "dev": true 105 | }, 106 | "node_modules/typescript": { 107 | "version": "5.9.3", 108 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", 109 | "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", 110 | "dev": true, 111 | "license": "Apache-2.0", 112 | "bin": { 113 | "tsc": "bin/tsc", 114 | "tsserver": "bin/tsserver" 115 | }, 116 | "engines": { 117 | "node": ">=14.17" 118 | } 119 | }, 120 | "node_modules/undici-types": { 121 | "version": "7.16.0", 122 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", 123 | "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", 124 | "dev": true, 125 | "license": "MIT" 126 | }, 127 | "node_modules/zod": { 128 | "version": "4.1.9", 129 | "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.9.tgz", 130 | "integrity": "sha512-HI32jTq0AUAC125z30E8bQNz0RQ+9Uc+4J7V97gLYjZVKRjeydPgGt6dvQzFrav7MYOUGFqqOGiHpA/fdbd0cQ==", 131 | "license": "MIT", 132 | "funding": { 133 | "url": "https://github.com/sponsors/colinhacks" 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zod-schema-faker 2 | 3 | > Generate mock data from [zod](https://github.com/colinhacks/zod) schemas. Powered by 4 | > [@faker-js/faker](https://github.com/faker-js/faker) and [randexp.js](https://github.com/fent/randexp.js). 5 | 6 | [![CI](https://github.com/soc221b/zod-schema-faker/actions/workflows/ci.yml/badge.svg)](https://github.com/soc221b/zod-schema-faker/actions/workflows/ci.yml) 7 | [![NPM](https://img.shields.io/npm/v/zod-schema-faker.svg?label=NPM&color=brightgreen)](https://www.npmjs.com/package/zod-schema-faker) 8 | 9 | Features 10 | 11 | - Support zod v3, v4 and mini 12 | - Support almost all zod types 13 | - Support for custom zod types 14 | - Extensive tests 15 | 16 | ## Installation 17 | 18 | ```sh 19 | npm install --save-dev zod-schema-faker 20 | ``` 21 | 22 | ## Usage 23 | 24 | ### Setup 25 | 26 | v3: 27 | 28 | ```ts 29 | import { install } from 'zod-schema-faker' // alias: 'zod-schema-faker/v3' 30 | 31 | install() 32 | ``` 33 | 34 | v4 or mini: 35 | 36 | ```ts 37 | import { setFaker } from 'zod-schema-faker/v4' 38 | import { faker } from '@faker-js/faker' 39 | 40 | setFaker(faker) 41 | ``` 42 | 43 | ### Fake Built-in types 44 | 45 | ```ts 46 | import { fake } from 'zod-schema-faker' 47 | 48 | const Player = z.object({ 49 | username: z.string(), 50 | xp: z.number(), 51 | }) 52 | 53 | const data = fake(Player) 54 | console.log(data) // { username: "billie", xp: 100 } 55 | ``` 56 | 57 | ### Fake Custom types 58 | 59 | v3: 60 | 61 | ```ts 62 | import { installCustom, fake, getFaker, ZodTypeFaker } from 'zod-schema-faker' 63 | 64 | // define a custom zod schema 65 | const pxSchema = z.custom<`${number}px`>(val => { 66 | return typeof val === 'string' ? /^\d+px$/.test(val) : false 67 | }) 68 | 69 | // define a custom faker 70 | class ZodPxFaker extends ZodTypeFaker { 71 | fake(): `${number}px` { 72 | return `${getFaker().number.int({ min: 0 })}px` 73 | } 74 | } 75 | 76 | // call installCustom() to register custom faker 77 | installCustom(pxSchema, ZodPxFaker) 78 | 79 | // generate fake data based on schema 80 | const data = fake(pxSchema) // '100px' 81 | ``` 82 | 83 | v4 or mini: 84 | 85 | ```ts 86 | import { custom, fake, Fake, getFaker } from 'zod-schema-faker/v4' 87 | 88 | // define a custom zod schema 89 | const pxSchema = z.custom<`${number}px`>(val => { 90 | return typeof val === 'string' ? /^\d+px$/.test(val) : false 91 | }) 92 | 93 | // define a custom faker 94 | const fakePxSchema: Fake = () => { 95 | return (getFaker().number.int({ min: 1, max: 100 }) + 'px') as `${number}px` 96 | } 97 | 98 | // call custom() to register custom faker 99 | custom(pxSchema, fakePxSchema) 100 | 101 | // generate fake data based on schema 102 | const data = fake(pxSchema) // '100px' 103 | ``` 104 | 105 | ## API 106 | 107 | ### v3 108 | 109 | #### Core APIs 110 | 111 | - `function install(): void`: Install fakers for built-in types, must be called before using `fake`. 112 | - `function fake(schema: T): z.infer`: Generate fake data based on schema. 113 | - `class ZodSchemaFakerError` 114 | 115 | #### Random Utility APIs 116 | 117 | - `function seed(value?: number): void`: Sets the seed to use. 118 | - `function setFaker(faker: Faker): void`: Use given faker instance instead of the default one. 119 | - `function getFaker(): Faker`: Get the faker instance. Defaults to `fakerEN`. 120 | - `function randexp(pattern: string | RegExp, flags?: string): string`: Create random strings that match a given regular 121 | expression. 122 | 123 | #### Customization APIs - see [example](./tests/zod-custom-faker.test.ts) for details 124 | 125 | - `class ZodTypeFaker`: Base class for fakers. 126 | - `function installCustom(schema: T, faker: typeof ZodTypeFakerConcrete): void`: Install 127 | fakers for custom schemas, must be called before using `fake`. 128 | 129 | ### v4 130 | 131 | #### Core APIs 132 | 133 | - `function fake(schema: T): core.infer`: Generate fake data based on schema. 134 | 135 | #### Random Utility APIs 136 | 137 | - `function seed(value?: number): void`: Sets the seed or generates a new one. This method is intended to allow for 138 | consistent values in tests, so you might want to use hardcoded values as the seed. 139 | - `function setFaker(faker: Faker): void`: Set the faker instance to use. 140 | - `function getFaker(): Faker`: Get the faker instance. 141 | - `function randexp(pattern: string | RegExp, flags?: string): string`: Create random strings that match a given regular 142 | expression. 143 | 144 | #### Customization APIs 145 | 146 | - `function custom(schema: T, fake: Fake): void`: Generate fake data based on schema. 147 | - `type Fake = (schema: T, context: Context, rootFake: RootFake) => core.infer`: Custom fake 148 | function. 149 | 150 | ## Unsupported 151 | 152 | ### v3 153 | 154 | - .refine ❌ 155 | - .superRefine ❌ 156 | 157 | ### v4 158 | 159 | - .codec 🚧 160 | - .file 🚧 161 | - .intersection 🚧 162 | - .preprocess 🚧 163 | - .refine ❌ 164 | - .stringbool custom 🚧 165 | - .stringFormat custom 🚧 166 | - .superRefine ❌ 167 | 168 | ## About 169 | 170 | Distributed under the MIT license. See LICENSE for more information. 171 | -------------------------------------------------------------------------------- /src/v4/internals/fake.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { fakeAny } from './schemas/any' 3 | import { fakeArray } from './schemas/array' 4 | import { fakeBigInt } from './schemas/big-int' 5 | import { fakeBoolean } from './schemas/boolean' 6 | import { fakeCatch } from './schemas/catch' 7 | import { fakeCustom } from './schemas/custom' 8 | import { fakeDate } from './schemas/date' 9 | import { fakeDefault } from './schemas/default' 10 | import { fakeEnum } from './schemas/enum' 11 | import { fakeFunction } from './schemas/function' 12 | import { fakeLazy } from './schemas/lazy' 13 | import { fakeLiteral } from './schemas/literal' 14 | import { fakeMap } from './schemas/map' 15 | import { fakeNaN } from './schemas/nan' 16 | import { fakeNever } from './schemas/never' 17 | import { fakeNonOptional } from './schemas/non-optional' 18 | import { fakeNull } from './schemas/null' 19 | import { fakeNullable } from './schemas/nullable' 20 | import { fakeNumber } from './schemas/number' 21 | import { fakeObject } from './schemas/object' 22 | import { fakeOptional } from './schemas/optional' 23 | import { fakePipe } from './schemas/pipe' 24 | import { fakePrefault } from './schemas/prefault' 25 | import { fakePromise } from './schemas/promise' 26 | import { fakeReadonly } from './schemas/readonly' 27 | import { fakeRecord } from './schemas/record' 28 | import { fakeSet } from './schemas/set' 29 | import { fakeString } from './schemas/string' 30 | import { fakeSymbol } from './schemas/symbol' 31 | import { fakeTemplateLiteral } from './schemas/template-literal' 32 | import { fakeTuple } from './schemas/tuple' 33 | import { fakeUndefined } from './schemas/undefined' 34 | import { fakeUnion } from './schemas/union' 35 | import { fakeUnknown } from './schemas/unknown' 36 | import { fakeVoid } from './schemas/void' 37 | import { RootFake } from './type' 38 | 39 | export const rootFake: RootFake = ((schema: core.$ZodType, context) => { 40 | switch (schema._zod.def.type) { 41 | case 'any': 42 | return fakeAny(schema as any, context, rootFake) 43 | case 'array': 44 | return fakeArray(schema as any, context, rootFake) 45 | case 'bigint': 46 | return fakeBigInt(schema as any, context, rootFake) 47 | case 'boolean': 48 | return fakeBoolean(schema as any, context, rootFake) 49 | case 'catch': 50 | return fakeCatch(schema as any, context, rootFake) 51 | case 'custom': 52 | return fakeCustom(schema as any, context, rootFake) 53 | case 'date': 54 | return fakeDate(schema as any, context, rootFake) 55 | case 'default': 56 | return fakeDefault(schema as any, context, rootFake) 57 | case 'enum': 58 | return fakeEnum(schema as any, context, rootFake) 59 | case 'function': 60 | return fakeFunction(schema as any, context, rootFake) 61 | case 'file': 62 | // TODO 63 | break 64 | case 'int': 65 | // TODO 66 | break 67 | case 'intersection': 68 | // TODO 69 | break 70 | case 'lazy': 71 | return fakeLazy(schema as any, context, rootFake) 72 | case 'literal': 73 | return fakeLiteral(schema as any, context, rootFake) 74 | case 'map': 75 | return fakeMap(schema as any, context, rootFake) 76 | case 'nan': 77 | return fakeNaN(schema as any, context, rootFake) 78 | case 'never': 79 | return fakeNever(schema as any, context, rootFake) 80 | case 'nonoptional': 81 | return fakeNonOptional(schema as any, context, rootFake) 82 | case 'null': 83 | return fakeNull(schema as any, context, rootFake) 84 | case 'nullable': 85 | return fakeNullable(schema as any, context, rootFake) 86 | case 'number': 87 | return fakeNumber(schema as any, context, rootFake) 88 | case 'object': 89 | return fakeObject(schema as any, context, rootFake) 90 | case 'optional': 91 | return fakeOptional(schema as any, context, rootFake) 92 | case 'pipe': 93 | return fakePipe(schema as any, context, rootFake) 94 | case 'prefault': 95 | return fakePrefault(schema as any, context, rootFake) 96 | case 'promise': 97 | return fakePromise(schema as any, context, rootFake) 98 | case 'readonly': 99 | return fakeReadonly(schema as any, context, rootFake) 100 | case 'record': 101 | return fakeRecord(schema as any, context, rootFake) 102 | case 'set': 103 | return fakeSet(schema as any, context, rootFake) 104 | case 'string': 105 | return fakeString(schema as any, context, rootFake) 106 | case 'success': 107 | // TODO 108 | break 109 | case 'symbol': 110 | return fakeSymbol(schema as any, context, rootFake) 111 | case 'template_literal': 112 | return fakeTemplateLiteral(schema as any, context, rootFake) 113 | case 'transform': 114 | // TODO 115 | break 116 | case 'tuple': 117 | return fakeTuple(schema as any, context, rootFake) 118 | case 'undefined': 119 | return fakeUndefined(schema as any, context, rootFake) 120 | case 'union': 121 | return fakeUnion(schema as any, context, rootFake) 122 | case 'unknown': 123 | return fakeUnknown(schema as any, context, rootFake) 124 | case 'void': 125 | return fakeVoid(schema as any, context, rootFake) 126 | default: { 127 | const _: never = schema._zod.def.type 128 | break 129 | } 130 | } 131 | 132 | throw TypeError() 133 | }) as RootFake 134 | -------------------------------------------------------------------------------- /src/v3/installation.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod/v3' 2 | import { zodFirstPartyTypeKindToZodTypeFaker, zodTypeToZodTypeFaker, type fake } from './fake' 3 | import { ZodAnyFaker } from './zod-any-faker' 4 | import { ZodArrayFaker } from './zod-array-faker' 5 | import { ZodBigIntFaker } from './zod-bigint-faker' 6 | import { ZodBooleanFaker } from './zod-boolean-faker' 7 | import { ZodBrandedFaker } from './zod-branded-faker' 8 | import { ZodCatchFaker } from './zod-catch-faker' 9 | import { ZodDateFaker } from './zod-date-faker' 10 | import { ZodDefaultFaker } from './zod-default-faker' 11 | import { ZodDiscriminatedUnionFaker } from './zod-discriminated-union-faker' 12 | import { ZodEffectsFaker } from './zod-effects-faker' 13 | import { ZodEnumFaker } from './zod-enum-faker' 14 | import { ZodFunctionFaker } from './zod-function-faker' 15 | import { ZodIntersectionFaker } from './zod-intersection-faker' 16 | import { ZodLazyFaker } from './zod-lazy-faker' 17 | import { ZodLiteralFaker } from './zod-literal-faker' 18 | import { ZodMapFaker } from './zod-map-faker' 19 | import { ZodNaNFaker } from './zod-nan-faker' 20 | import { ZodNativeEnumFaker } from './zod-native-enum-faker' 21 | import { ZodNeverFaker } from './zod-never-faker' 22 | import { ZodNullFaker } from './zod-null-faker' 23 | import { ZodNullableFaker } from './zod-nullable-faker' 24 | import { ZodNumberFaker } from './zod-number-faker' 25 | import { ZodObjectFaker } from './zod-object-faker' 26 | import { ZodOptionalFaker } from './zod-optional-faker' 27 | import { ZodPipelineFaker } from './zod-pipe-faker' 28 | import { ZodPromiseFaker } from './zod-promise-faker' 29 | import { ZodReadonlyFaker } from './zod-readonly-faker' 30 | import { ZodRecordFaker } from './zod-record-faker' 31 | import { ZodSetFaker } from './zod-set-faker' 32 | import { ZodStringFaker } from './zod-string-faker' 33 | import { ZodSymbolFaker } from './zod-symbol-faker' 34 | import { ZodTupleFaker } from './zod-tuple-faker' 35 | import { ZodTypeFakerConcrete } from './zod-type-faker' 36 | import { ZodUndefinedFaker } from './zod-undefined-faker' 37 | import { ZodUnionFaker } from './zod-union-faker' 38 | import { ZodUnknownFaker } from './zod-unknown-faker' 39 | import { ZodVoidFaker } from './zod-void-faker' 40 | 41 | /** 42 | * Install fakers for built-in types, must be called before using {@link fake}. 43 | */ 44 | export function install(): void { 45 | const exhaustiveZodFirstPartyTypeKindToZodTypeFaker: Record< 46 | z.ZodFirstPartyTypeKind, 47 | typeof ZodTypeFakerConcrete 48 | > = { 49 | [z.ZodFirstPartyTypeKind.ZodAny]: ZodAnyFaker, 50 | [z.ZodFirstPartyTypeKind.ZodArray]: ZodArrayFaker, 51 | [z.ZodFirstPartyTypeKind.ZodBigInt]: ZodBigIntFaker, 52 | [z.ZodFirstPartyTypeKind.ZodBoolean]: ZodBooleanFaker, 53 | [z.ZodFirstPartyTypeKind.ZodBranded]: ZodBrandedFaker, 54 | [z.ZodFirstPartyTypeKind.ZodCatch]: ZodCatchFaker, 55 | [z.ZodFirstPartyTypeKind.ZodDate]: ZodDateFaker, 56 | [z.ZodFirstPartyTypeKind.ZodDefault]: ZodDefaultFaker, 57 | [z.ZodFirstPartyTypeKind.ZodDiscriminatedUnion]: ZodDiscriminatedUnionFaker, 58 | [z.ZodFirstPartyTypeKind.ZodEffects]: ZodEffectsFaker, 59 | [z.ZodFirstPartyTypeKind.ZodEnum]: ZodEnumFaker, 60 | [z.ZodFirstPartyTypeKind.ZodFunction]: ZodFunctionFaker, 61 | [z.ZodFirstPartyTypeKind.ZodIntersection]: ZodIntersectionFaker, 62 | [z.ZodFirstPartyTypeKind.ZodLazy]: ZodLazyFaker, 63 | [z.ZodFirstPartyTypeKind.ZodLiteral]: ZodLiteralFaker, 64 | [z.ZodFirstPartyTypeKind.ZodMap]: ZodMapFaker, 65 | [z.ZodFirstPartyTypeKind.ZodNaN]: ZodNaNFaker, 66 | [z.ZodFirstPartyTypeKind.ZodNativeEnum]: ZodNativeEnumFaker, 67 | [z.ZodFirstPartyTypeKind.ZodNever]: ZodNeverFaker, 68 | [z.ZodFirstPartyTypeKind.ZodNull]: ZodNullFaker, 69 | [z.ZodFirstPartyTypeKind.ZodNullable]: ZodNullableFaker, 70 | [z.ZodFirstPartyTypeKind.ZodNumber]: ZodNumberFaker, 71 | [z.ZodFirstPartyTypeKind.ZodObject]: ZodObjectFaker, 72 | [z.ZodFirstPartyTypeKind.ZodOptional]: ZodOptionalFaker, 73 | [z.ZodFirstPartyTypeKind.ZodPipeline]: ZodPipelineFaker, 74 | [z.ZodFirstPartyTypeKind.ZodPromise]: ZodPromiseFaker, 75 | [z.ZodFirstPartyTypeKind.ZodReadonly]: ZodReadonlyFaker, 76 | [z.ZodFirstPartyTypeKind.ZodRecord]: ZodRecordFaker, 77 | [z.ZodFirstPartyTypeKind.ZodSet]: ZodSetFaker, 78 | [z.ZodFirstPartyTypeKind.ZodString]: ZodStringFaker, 79 | [z.ZodFirstPartyTypeKind.ZodSymbol]: ZodSymbolFaker, 80 | [z.ZodFirstPartyTypeKind.ZodTuple]: ZodTupleFaker, 81 | [z.ZodFirstPartyTypeKind.ZodUndefined]: ZodUndefinedFaker, 82 | [z.ZodFirstPartyTypeKind.ZodUnion]: ZodUnionFaker, 83 | [z.ZodFirstPartyTypeKind.ZodUnknown]: ZodUnknownFaker, 84 | [z.ZodFirstPartyTypeKind.ZodVoid]: ZodVoidFaker, 85 | } 86 | for (const zoDFirstPartyType of Object.keys( 87 | exhaustiveZodFirstPartyTypeKindToZodTypeFaker, 88 | ) as z.ZodFirstPartyTypeKind[]) { 89 | zodFirstPartyTypeKindToZodTypeFaker.set( 90 | zoDFirstPartyType, 91 | exhaustiveZodFirstPartyTypeKindToZodTypeFaker[zoDFirstPartyType], 92 | ) 93 | } 94 | } 95 | 96 | /** 97 | * Install fakers for custom schemas, must be called before using {@link fake}. 98 | */ 99 | export function installCustom(schema: T, faker: typeof ZodTypeFakerConcrete): void { 100 | zodTypeToZodTypeFaker.set(schema, faker) 101 | } 102 | 103 | /** 104 | * @internal This is a private API, do not use. It is a utility function for testing. 105 | */ 106 | export const uninstall = (): void => { 107 | zodFirstPartyTypeKindToZodTypeFaker.clear() 108 | zodTypeToZodTypeFaker.clear() 109 | } 110 | -------------------------------------------------------------------------------- /tests/v3/zod-bigint-faker.test.ts: -------------------------------------------------------------------------------- 1 | import { expectType, TypeEqual } from 'ts-expect' 2 | import { describe, expect, test } from 'vitest' 3 | import { z } from 'zod/v3' 4 | import { getFaker } from '../../src/v3' 5 | import { ZodBigIntFaker } from '../../src/v3/zod-bigint-faker' 6 | import { testMultipleTimes } from './util' 7 | 8 | test('ZodBigIntFaker should assert parameters', () => { 9 | const invalidSchema = void 0 as any 10 | expect(() => new ZodBigIntFaker(invalidSchema)).toThrow() 11 | }) 12 | 13 | test('ZodBigIntFaker should accepts a ZodBigInt schema', () => { 14 | const schema = z.bigint() 15 | expect(() => new ZodBigIntFaker(schema)).not.toThrow() 16 | }) 17 | 18 | test('ZodBigIntFaker should return a ZodBigIntFaker instance', () => { 19 | const schema = z.bigint() 20 | const faker = new ZodBigIntFaker(schema) 21 | expect(faker instanceof ZodBigIntFaker).toBe(true) 22 | }) 23 | 24 | test('ZodBigIntFaker.fake should be a function', () => { 25 | const schema = z.bigint() 26 | const faker = new ZodBigIntFaker(schema) 27 | expect(typeof faker.fake).toBe('function') 28 | }) 29 | 30 | test('ZodBigIntFaker.fake should return bigint type', () => { 31 | const schema = z.bigint() 32 | const faker = new ZodBigIntFaker(schema) 33 | expectType, bigint>>(true) 34 | }) 35 | 36 | test('ZodBigIntFaker.fake should return a valid data', () => { 37 | const schema = z.bigint() 38 | const faker = new ZodBigIntFaker(schema) 39 | const data = faker.fake() 40 | expect(schema.safeParse(data).success).toBe(true) 41 | }) 42 | 43 | test('gt', () => { 44 | const schema = z.bigint().gt(100n) 45 | const faker = new ZodBigIntFaker(schema) 46 | const data = faker.fake() 47 | expect(schema.safeParse(data).success).toBe(true) 48 | }) 49 | 50 | test('gte', () => { 51 | const schema = z.bigint().gte(100n) 52 | const faker = new ZodBigIntFaker(schema) 53 | const data = faker.fake() 54 | expect(schema.safeParse(data).success).toBe(true) 55 | }) 56 | 57 | test('lt', () => { 58 | const schema = z.bigint().lt(100n) 59 | const faker = new ZodBigIntFaker(schema) 60 | const data = faker.fake() 61 | expect(schema.safeParse(data).success).toBe(true) 62 | }) 63 | 64 | test('lte', () => { 65 | const schema = z.bigint().lte(100n) 66 | const faker = new ZodBigIntFaker(schema) 67 | const data = faker.fake() 68 | expect(schema.safeParse(data).success).toBe(true) 69 | }) 70 | 71 | test('positive', () => { 72 | const schema = z.bigint().positive() 73 | const faker = new ZodBigIntFaker(schema) 74 | const data = faker.fake() 75 | expect(schema.safeParse(data).success).toBe(true) 76 | }) 77 | 78 | test('nonnegative', () => { 79 | const schema = z.bigint().nonnegative() 80 | const faker = new ZodBigIntFaker(schema) 81 | const data = faker.fake() 82 | expect(schema.safeParse(data).success).toBe(true) 83 | }) 84 | 85 | test('negative', () => { 86 | const schema = z.bigint().negative() 87 | const faker = new ZodBigIntFaker(schema) 88 | const data = faker.fake() 89 | expect(schema.safeParse(data).success).toBe(true) 90 | }) 91 | 92 | test('nonpositive', () => { 93 | const schema = z.bigint().nonpositive() 94 | const faker = new ZodBigIntFaker(schema) 95 | const data = faker.fake() 96 | expect(schema.safeParse(data).success).toBe(true) 97 | }) 98 | 99 | test('multiplyOf positive', () => { 100 | const schema = z.bigint().multipleOf(37n) 101 | const faker = new ZodBigIntFaker(schema) 102 | const data = faker.fake() 103 | expect(schema.safeParse(data).success).toBe(true) 104 | }) 105 | 106 | test('multiplyOf negative', () => { 107 | const schema = z.bigint().multipleOf(-37n) 108 | const faker = new ZodBigIntFaker(schema) 109 | const data = faker.fake() 110 | if (schema.safeParse(data).success === false) { 111 | console.log(data) 112 | } 113 | expect(schema.safeParse(data).success).toBe(true) 114 | }) 115 | 116 | describe('edge case', () => { 117 | test('gt', () => { 118 | const schema = z.bigint().gt(100n).lte(101n) 119 | const faker = new ZodBigIntFaker(schema) 120 | const data = faker.fake() 121 | expect(schema.safeParse(data).success).toBe(true) 122 | expect(data).toBe(101n) 123 | }) 124 | 125 | test('gte', () => { 126 | const schema = z.bigint().gte(100n).lt(101n) 127 | const faker = new ZodBigIntFaker(schema) 128 | const data = faker.fake() 129 | expect(schema.safeParse(data).success).toBe(true) 130 | expect(data).toBe(100n) 131 | }) 132 | 133 | test('lt', () => { 134 | const schema = z.bigint().lt(100n).gte(99n) 135 | const faker = new ZodBigIntFaker(schema) 136 | const data = faker.fake() 137 | expect(schema.safeParse(data).success).toBe(true) 138 | expect(data).toBe(99n) 139 | }) 140 | 141 | test('lte', () => { 142 | const schema = z.bigint().lte(100n).gt(99n) 143 | const faker = new ZodBigIntFaker(schema) 144 | const data = faker.fake() 145 | expect(schema.safeParse(data).success).toBe(true) 146 | expect(data).toBe(100n) 147 | }) 148 | 149 | test('positive', () => { 150 | const schema = z.bigint().positive().lt(2n) 151 | const faker = new ZodBigIntFaker(schema) 152 | const data = faker.fake() 153 | expect(schema.safeParse(data).success).toBe(true) 154 | expect(data).toBe(1n) 155 | }) 156 | 157 | test('nonnegative', () => { 158 | const schema = z.bigint().nonnegative().lt(1n) 159 | const faker = new ZodBigIntFaker(schema) 160 | const data = faker.fake() 161 | expect(schema.safeParse(data).success).toBe(true) 162 | expect(data).toBe(0n) 163 | }) 164 | 165 | test('negative', () => { 166 | const schema = z.bigint().negative().gt(-2n) 167 | const faker = new ZodBigIntFaker(schema) 168 | const data = faker.fake() 169 | expect(schema.safeParse(data).success).toBe(true) 170 | expect(data).toBe(-1n) 171 | }) 172 | 173 | test('nonpositive', () => { 174 | const schema = z.bigint().nonpositive().gt(-1n) 175 | const faker = new ZodBigIntFaker(schema) 176 | const data = faker.fake() 177 | expect(schema.safeParse(data).success).toBe(true) 178 | expect(data).toBe(0n) 179 | }) 180 | 181 | testMultipleTimes('integration', () => { 182 | const min = getFaker().number.bigInt({ min: -1000n, max: 1000n }) 183 | const max = getFaker().number.bigInt({ min, max: min + 1000n }) 184 | const diff = max - min 185 | const multipleOf = getFaker().number.bigInt({ min: 1n, max: diff + 1n }) 186 | const schema = z.bigint().multipleOf(multipleOf).min(min).max(max) 187 | const faker = new ZodBigIntFaker(schema) 188 | const data = faker.fake() 189 | expect(schema.safeParse(data).success).toBe(true) 190 | }) 191 | }) 192 | 193 | describe('multiple checks of the same kind', () => { 194 | test('multiple min', () => { 195 | const schema = z.bigint().min(200n).min(300n).min(100n).max(300n) 196 | const faker = new ZodBigIntFaker(schema) 197 | const data = faker.fake() 198 | expect(schema.safeParse(data).data).toBe(300n) 199 | }) 200 | 201 | test('multiple max', () => { 202 | const schema = z.bigint().max(100n).max(300n).max(200n).min(100n) 203 | const faker = new ZodBigIntFaker(schema) 204 | const data = faker.fake() 205 | expect(schema.safeParse(data).data).toBe(100n) 206 | }) 207 | 208 | test('multiple multipleOf', () => { 209 | const schema = z.bigint().multipleOf(2n).multipleOf(3n).min(2n).max(6n) 210 | const faker = new ZodBigIntFaker(schema) 211 | const data = faker.fake() 212 | expect(schema.safeParse(data).data).toBe(6n) 213 | }) 214 | }) 215 | 216 | describe('impossible case', () => { 217 | test('min > max', () => { 218 | const schema = z.bigint().min(100n).max(99n) 219 | const faker = new ZodBigIntFaker(schema) 220 | expect(() => faker.fake()).toThrow(RangeError) 221 | }) 222 | 223 | test('min > max', () => { 224 | const schema = z.bigint().min(100n).max(99n) 225 | const faker = new ZodBigIntFaker(schema) 226 | expect(() => faker.fake()).toThrow(RangeError) 227 | }) 228 | }) 229 | -------------------------------------------------------------------------------- /src/v4/internals/schemas/checks/string-format.ts: -------------------------------------------------------------------------------- 1 | import * as core from 'zod/v4/core' 2 | import { HashFormat } from 'zod/v4/core/util' 3 | import { rootFake as internalFake } from '../../fake' 4 | import { getFaker, randexp } from '../../random' 5 | 6 | const practicalEmailRegex = 7 | /^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/ 8 | 9 | export function fakeStringFormat( 10 | schema: T, 11 | rootFake: typeof internalFake, 12 | ): undefined | string { 13 | let data = undefined 14 | const format = schema._zod.def.format as core.$ZodStringFormats | 'hex' | 'hostname' | 'httpUrl' | HashFormat 15 | switch (format) { 16 | case 'base64': { 17 | data = randexp(core.regexes.base64) 18 | break 19 | } 20 | case 'base64url': { 21 | // the regex itself is insufficient to validate a base64url, see https://github.com/colinhacks/zod/blob/923af801fde9f033cfd7e0e753b421a554fe3be8/packages/zod/src/v4/core/schemas.ts#L903-L905 22 | // data = randexp(core.regexes.base64url) 23 | // TODO: generate random base64url 24 | data = 'Zm9vK2Jhci9iYXo' 25 | break 26 | } 27 | case 'cidrv4': { 28 | data = randexp(core.regexes.cidrv4) 29 | break 30 | } 31 | case 'cidrv6': { 32 | data = getFaker().internet.ipv6() + '/' + getFaker().number.int({ min: 0, max: 128 }) 33 | break 34 | } 35 | case 'cuid': { 36 | data = randexp(core.regexes.cuid) 37 | break 38 | } 39 | case 'cuid2': { 40 | data = randexp(core.regexes.cuid2) 41 | break 42 | } 43 | case 'date': { 44 | data = randexp(core.regexes.date) 45 | break 46 | } 47 | case 'datetime': { 48 | const def = schema._zod.def as core.$ZodISODateTimeDef 49 | data = randexp( 50 | core.regexes.datetime({ 51 | local: def.local, 52 | offset: def.offset, 53 | precision: def.precision, 54 | }), 55 | ) 56 | break 57 | } 58 | case 'duration': { 59 | data = [ 60 | 'P', 61 | getFaker().datatype.boolean() ? getFaker().number.int({ min: 0, max: 10 * 2000 }) + 'Y' : '', 62 | getFaker().datatype.boolean() ? getFaker().number.int({ min: 0, max: 10 * 12 }) + 'M' : '', 63 | getFaker().datatype.boolean() ? getFaker().number.int({ min: 0, max: 10 * 31 }) + 'D' : '', 64 | 'T', 65 | getFaker().datatype.boolean() ? getFaker().number.int({ min: 0, max: 10 * 24 }) + 'H' : '', 66 | getFaker().datatype.boolean() ? getFaker().number.int({ min: 0, max: 10 * 60 }) + 'M' : '', 67 | getFaker().datatype.boolean() ? getFaker().number.int({ min: 0, max: 10 * 60 }) + 'S' : '', 68 | ] 69 | .join('') 70 | // PnYT => PnY 71 | .replace(/T$/, '') 72 | // P => PnW 73 | .replace(/^P$/, 'P' + getFaker().number.int({ min: 0, max: 100 }) + 'W') 74 | break 75 | } 76 | case 'e164': { 77 | data = randexp(core.regexes.e164) 78 | break 79 | } 80 | case 'email': { 81 | const regex = schema._zod.def.pattern ? schema._zod.def.pattern : core.regexes.email 82 | data = randexp(regex) 83 | if (practicalEmailRegex.source === regex.source) { 84 | data = data.replace(/\.\./g, '.').replace(/^\./, 'a') 85 | } 86 | break 87 | } 88 | case 'emoji': { 89 | data = '☘' 90 | break 91 | } 92 | case 'hex': { 93 | data = randexp(core.regexes.hex) 94 | break 95 | } 96 | case 'hostname': { 97 | data = getFaker().internet.domainName() 98 | break 99 | } 100 | case 'httpUrl': { 101 | data = getFaker().internet.url() 102 | break 103 | } 104 | case 'guid': { 105 | data = randexp(core.regexes.guid) 106 | break 107 | } 108 | case 'ipv4': { 109 | data = randexp(core.regexes.ipv4) 110 | break 111 | } 112 | case 'ipv6': { 113 | data = getFaker().internet.ipv6() 114 | break 115 | } 116 | case 'jwt': { 117 | const def = schema._zod.def as core.$ZodJWTDef 118 | data = getFaker().internet.jwt({ 119 | header: { 120 | alg: def.alg || getFaker().internet.jwtAlgorithm(), 121 | typ: 'JWT', 122 | }, 123 | }) 124 | break 125 | } 126 | case 'ksuid': { 127 | data = randexp(core.regexes.ksuid) 128 | break 129 | } 130 | case 'md5_base64': { 131 | data = randexp(core.regexes.md5_base64) 132 | break 133 | } 134 | case 'md5_base64url': { 135 | data = randexp(core.regexes.md5_base64url) 136 | break 137 | } 138 | case 'md5_hex': { 139 | data = randexp(core.regexes.md5_hex) 140 | break 141 | } 142 | case 'nanoid': { 143 | data = randexp(core.regexes.nanoid) 144 | break 145 | } 146 | case 'regex': { 147 | data = randexp(schema._zod.def.pattern!) 148 | break 149 | } 150 | case 'sha1_base64': { 151 | data = randexp(core.regexes.sha1_base64) 152 | break 153 | } 154 | case 'sha1_base64url': { 155 | data = randexp(core.regexes.sha1_base64url) 156 | break 157 | } 158 | case 'sha1_hex': { 159 | data = randexp(core.regexes.sha1_hex) 160 | break 161 | } 162 | case 'sha256_base64': { 163 | data = randexp(core.regexes.sha256_base64) 164 | break 165 | } 166 | case 'sha256_base64url': { 167 | data = randexp(core.regexes.sha256_base64url) 168 | break 169 | } 170 | case 'sha256_hex': { 171 | data = randexp(core.regexes.sha256_hex) 172 | break 173 | } 174 | case 'sha384_base64': { 175 | data = randexp(core.regexes.sha384_base64) 176 | break 177 | } 178 | case 'sha384_base64url': { 179 | data = randexp(core.regexes.sha384_base64url) 180 | break 181 | } 182 | case 'sha384_hex': { 183 | data = randexp(core.regexes.sha384_hex) 184 | break 185 | } 186 | case 'sha512_base64': { 187 | data = randexp(core.regexes.sha512_base64) 188 | break 189 | } 190 | case 'sha512_base64url': { 191 | data = randexp(core.regexes.sha512_base64url) 192 | break 193 | } 194 | case 'sha512_hex': { 195 | data = randexp(core.regexes.sha512_hex) 196 | break 197 | } 198 | case 'time': { 199 | const def = schema._zod.def as core.$ZodISOTimeDef 200 | data = randexp( 201 | core.regexes.time({ 202 | precision: def.precision, 203 | }), 204 | ) 205 | break 206 | } 207 | case 'ulid': { 208 | data = randexp(core.regexes.ulid) 209 | break 210 | } 211 | case 'url': { 212 | const protocol = (schema as core.$ZodURL)._zod.def.protocol 213 | const hostname = (schema as core.$ZodURL)._zod.def.hostname 214 | if (protocol && hostname) { 215 | data = randexp(protocol) + '://' + randexp(hostname) 216 | } else if (protocol) { 217 | data = randexp(protocol) + '://' + getFaker().internet.domainName() 218 | } else if (hostname) { 219 | data = 'https://' + randexp(hostname) 220 | } else { 221 | data = getFaker().internet.url() 222 | } 223 | break 224 | } 225 | case 'uuid': { 226 | const version = (schema as core.$ZodUUID)._zod.def.version 227 | switch (version) { 228 | case 'v1': 229 | data = randexp(core.regexes.uuid(1)) 230 | break 231 | case 'v2': 232 | data = randexp(core.regexes.uuid(2)) 233 | break 234 | case 'v3': 235 | data = randexp(core.regexes.uuid(3)) 236 | break 237 | case 'v4': 238 | data = randexp(core.regexes.uuid(4)) 239 | break 240 | case 'v5': 241 | data = randexp(core.regexes.uuid(5)) 242 | break 243 | case 'v6': 244 | data = randexp(core.regexes.uuid(6)) 245 | break 246 | case 'v7': 247 | data = randexp(core.regexes.uuid(7)) 248 | break 249 | case 'v8': 250 | data = randexp(core.regexes.uuid(8)) 251 | break 252 | default: { 253 | const _: undefined = version 254 | data = randexp(core.regexes.uuid()) 255 | } 256 | } 257 | break 258 | } 259 | case 'xid': { 260 | data = randexp(core.regexes.xid) 261 | break 262 | } 263 | default: { 264 | const _: 'ends_with' | 'includes' | 'json_string' | 'lowercase' | 'starts_with' | 'uppercase' = format 265 | break 266 | } 267 | } 268 | return data 269 | } 270 | --------------------------------------------------------------------------------