diff --git a/.github/workflows/validations.yml b/.github/workflows/validations.yml index e2e703ebf..86de13a3f 100644 --- a/.github/workflows/validations.yml +++ b/.github/workflows/validations.yml @@ -49,7 +49,7 @@ jobs: name: dist - name: Add dependencies run: | - yarn add express@${{matrix.express-version}} typescript@5.1 http-errors zod@^4.0.0-beta.20250505T195954 + yarn add express@${{matrix.express-version}} typescript@5.1 http-errors zod@next yarn add -D eslint@9.0 typescript-eslint@8.0 vitest tsx yarn add express-zod-api@./dist.tgz - name: Run tests diff --git a/CHANGELOG.md b/CHANGELOG.md index f99c7a991..8dace0daf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,8 @@ ### v24.0.0 - Switched to Zod 4: - - Minimum supported version of `zod` is 4.0.0; - - ⚠️This version might not support all new features of Zod 4; + - Minimum supported version of `zod` is 3.25.0, BUT imports MUST be from `zod/v4`; + - Find out why it's so weird here: https://github.com/colinhacks/zod/issues/4371 - `IOSchema` type had to be simplified down to a schema resulting to an `object`, but not an `array`; - Despite supporting examples by the new Zod method `.meta()`, users should still use `.example()` to set them; - Refer to [Migration guide on Zod 4](https://v4.zod.dev/v4/changelog) for adjusting your schemas; diff --git a/README.md b/README.md index fb0dbb102..1947b0dbf 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ import { defaultEndpointsFactory } from "express-zod-api"; The endpoint responds with "Hello, World" or "Hello, {name}" if the name is supplied within `GET` request payload. ```typescript -import { z } from "zod"; +import { z } from "zod/v4"; const helloWorldEndpoint = defaultEndpointsFactory.build({ // method: "get" (default) or array ["get", "post", ...] @@ -325,7 +325,7 @@ Inputs of middlewares are also available to endpoint handlers within `input`. Here is an example of the authentication middleware, that checks a `key` from input and `token` from headers: ```typescript -import { z } from "zod"; +import { z } from "zod/v4"; import createHttpError from "http-errors"; import { Middleware } from "express-zod-api"; @@ -455,7 +455,7 @@ You can implement additional validations within schemas using refinements. Validation errors are reported in a response with a status code `400`. ```typescript -import { z } from "zod"; +import { z } from "zod/v4"; import { Middleware } from "express-zod-api"; const nicknameConstraintMiddleware = new Middleware({ @@ -496,7 +496,7 @@ Since parameters of GET requests come in the form of strings, there is often a n arrays of numbers. ```typescript -import { z } from "zod"; +import { z } from "zod/v4"; const getUserEndpoint = endpointsFactory.build({ input: z.object({ @@ -527,7 +527,7 @@ Here is a recommended solution: it is important to use shallow transformations o ```ts import camelize from "camelize-ts"; import snakify from "snakify-ts"; -import { z } from "zod"; +import { z } from "zod/v4"; const endpoint = endpointsFactory.build({ input: z @@ -580,7 +580,7 @@ provides your endpoint handler or middleware with a `Date`. It supports the foll format for the response transmission. Consider the following simplified example for better understanding: ```typescript -import { z } from "zod"; +import { z } from "zod/v4"; import { ez, defaultEndpointsFactory } from "express-zod-api"; const updateUserEndpoint = defaultEndpointsFactory.build({ @@ -790,7 +790,7 @@ In a similar way you can enable request headers as the input source. This is an ```typescript import { createConfig, Middleware } from "express-zod-api"; -import { z } from "zod"; +import { z } from "zod/v4"; createConfig({ inputSources: { @@ -825,7 +825,7 @@ type DefaultResponse = You can create your own result handler by using this example as a template: ```typescript -import { z } from "zod"; +import { z } from "zod/v4"; import { ResultHandler, ensureHttpError, @@ -951,7 +951,7 @@ which is `express.urlencoded()` by default. The request content type should be ` ```ts import { defaultEndpointsFactory, ez } from "express-zod-api"; -import { z } from "zod"; +import { z } from "zod/v4"; export const submitFeedbackEndpoint = defaultEndpointsFactory.build({ method: "post", @@ -991,7 +991,7 @@ const config = createConfig({ Then use `ez.upload()` schema for a corresponding property. The request content type must be `multipart/form-data`: ```typescript -import { z } from "zod"; +import { z } from "zod/v4"; import { ez, defaultEndpointsFactory } from "express-zod-api"; const fileUploadEndpoint = defaultEndpointsFactory.build({ @@ -1069,7 +1069,7 @@ from outputs of previous middlewares, if the one being tested somehow depends on either by `errorHandler` configured within given `configProps` or `defaultResultHandler`. ```typescript -import { z } from "zod"; +import { z } from "zod/v4"; import { Middleware, testMiddleware } from "express-zod-api"; const middleware = new Middleware({ @@ -1180,7 +1180,7 @@ Client application can subscribe to the event stream using `EventSource` class i the implementation emitting the `time` event each second. ```typescript -import { z } from "zod"; +import { z } from "zod/v4"; import { EventStreamFactory } from "express-zod-api"; import { setTimeout } from "node:timers/promises"; @@ -1320,7 +1320,7 @@ You can also deprecate all routes the `Endpoint` assigned to by setting `Endpoin ```ts import { Routing, DependsOnMethod } from "express-zod-api"; -import { z } from "zod"; +import { z } from "zod/v4"; const someEndpoint = factory.build({ deprecated: true, // deprecates all routes the endpoint assigned to @@ -1345,7 +1345,7 @@ need to reuse a handling rule for multiple brands, use the exposed types `Depict ```ts import ts from "typescript"; -import { z } from "zod"; +import { z } from "zod/v4"; import { Documentation, Integration, @@ -1391,7 +1391,7 @@ in this case during development. You can achieve this verification by assigning reusing it in forced type of the output: ```typescript -import { z } from "zod"; +import { z } from "zod/v4"; const output = z.object({ anything: z.number(), diff --git a/example/endpoints/accept-raw.ts b/example/endpoints/accept-raw.ts index 3b84eba20..d328436da 100644 --- a/example/endpoints/accept-raw.ts +++ b/example/endpoints/accept-raw.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { defaultEndpointsFactory, ez } from "express-zod-api"; export const rawAcceptingEndpoint = defaultEndpointsFactory.build({ diff --git a/example/endpoints/create-user.ts b/example/endpoints/create-user.ts index c48b09a08..56b4260f0 100644 --- a/example/endpoints/create-user.ts +++ b/example/endpoints/create-user.ts @@ -1,6 +1,6 @@ import createHttpError from "http-errors"; import assert from "node:assert/strict"; -import { z } from "zod"; +import { z } from "zod/v4"; import { statusDependingFactory } from "../factories"; /** @desc depending on the thrown error, the custom result handler of the factory responds slightly differently */ diff --git a/example/endpoints/delete-user.ts b/example/endpoints/delete-user.ts index e655a3e4a..09053766c 100644 --- a/example/endpoints/delete-user.ts +++ b/example/endpoints/delete-user.ts @@ -1,6 +1,6 @@ import createHttpError from "http-errors"; import assert from "node:assert/strict"; -import { z } from "zod"; +import { z } from "zod/v4"; import { noContentFactory } from "../factories"; /** @desc The endpoint demonstrates no content response established by its factory */ diff --git a/example/endpoints/list-users.ts b/example/endpoints/list-users.ts index c99e2a57a..4a6313519 100644 --- a/example/endpoints/list-users.ts +++ b/example/endpoints/list-users.ts @@ -1,4 +1,4 @@ -import z from "zod"; +import z from "zod/v4"; import { arrayRespondingFactory } from "../factories"; /** diff --git a/example/endpoints/retrieve-user.ts b/example/endpoints/retrieve-user.ts index 838ee69b1..2f08cb251 100644 --- a/example/endpoints/retrieve-user.ts +++ b/example/endpoints/retrieve-user.ts @@ -1,6 +1,6 @@ import createHttpError from "http-errors"; import assert from "node:assert/strict"; -import { z } from "zod"; +import { z } from "zod/v4"; import { defaultEndpointsFactory } from "express-zod-api"; import { methodProviderMiddleware } from "../middlewares"; diff --git a/example/endpoints/send-avatar.ts b/example/endpoints/send-avatar.ts index 9bc91b442..6f6890b13 100644 --- a/example/endpoints/send-avatar.ts +++ b/example/endpoints/send-avatar.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { fileSendingEndpointsFactory } from "../factories"; import { readFile } from "node:fs/promises"; diff --git a/example/endpoints/stream-avatar.ts b/example/endpoints/stream-avatar.ts index 677edc57b..2444741d5 100644 --- a/example/endpoints/stream-avatar.ts +++ b/example/endpoints/stream-avatar.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { fileStreamingEndpointsFactory } from "../factories"; export const streamAvatarEndpoint = fileStreamingEndpointsFactory.build({ diff --git a/example/endpoints/submit-feedback.ts b/example/endpoints/submit-feedback.ts index cc70895aa..24388721b 100644 --- a/example/endpoints/submit-feedback.ts +++ b/example/endpoints/submit-feedback.ts @@ -1,5 +1,5 @@ import { defaultEndpointsFactory, ez } from "express-zod-api"; -import { z } from "zod"; +import { z } from "zod/v4"; export const submitFeedbackEndpoint = defaultEndpointsFactory.build({ method: "post", diff --git a/example/endpoints/time-subscription.ts b/example/endpoints/time-subscription.ts index ca8625630..ef2e54135 100644 --- a/example/endpoints/time-subscription.ts +++ b/example/endpoints/time-subscription.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { setTimeout } from "node:timers/promises"; import { eventsFactory } from "../factories"; diff --git a/example/endpoints/update-user.ts b/example/endpoints/update-user.ts index 2d86697a0..bcbc3b110 100644 --- a/example/endpoints/update-user.ts +++ b/example/endpoints/update-user.ts @@ -1,6 +1,6 @@ import createHttpError from "http-errors"; import assert from "node:assert/strict"; -import { z } from "zod"; +import { z } from "zod/v4"; import { ez } from "express-zod-api"; import { keyAndTokenAuthenticatedEndpointsFactory } from "../factories"; diff --git a/example/endpoints/upload-avatar.ts b/example/endpoints/upload-avatar.ts index 671c9da78..28e1e84d7 100644 --- a/example/endpoints/upload-avatar.ts +++ b/example/endpoints/upload-avatar.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { defaultEndpointsFactory, ez } from "express-zod-api"; import { createHash } from "node:crypto"; diff --git a/example/example.documentation.yaml b/example/example.documentation.yaml index a0ec40b6f..31a78f557 100644 --- a/example/example.documentation.yaml +++ b/example/example.documentation.yaml @@ -271,7 +271,7 @@ paths: id: type: integer exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 + maximum: 9007199254740991 required: - id required: @@ -293,7 +293,7 @@ paths: id: type: integer exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 + maximum: 9007199254740991 required: - id required: @@ -613,7 +613,7 @@ paths: data: type: integer exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 + maximum: 9007199254740991 event: type: string const: time @@ -622,7 +622,7 @@ paths: retry: type: integer exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 + maximum: 9007199254740991 required: - data - event @@ -676,7 +676,7 @@ paths: crc: type: integer exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 + maximum: 9007199254740991 required: - crc required: diff --git a/example/factories.ts b/example/factories.ts index 3eed6809c..3d807df23 100644 --- a/example/factories.ts +++ b/example/factories.ts @@ -9,7 +9,7 @@ import { } from "express-zod-api"; import { authMiddleware } from "./middlewares"; import { createReadStream } from "node:fs"; -import { z } from "zod"; +import { z } from "zod/v4"; /** @desc This factory extends the default one by enforcing the authentication using the specified middleware */ export const keyAndTokenAuthenticatedEndpointsFactory = diff --git a/example/middlewares.ts b/example/middlewares.ts index e3b68021f..2fa2351fe 100644 --- a/example/middlewares.ts +++ b/example/middlewares.ts @@ -1,6 +1,6 @@ import createHttpError from "http-errors"; import assert from "node:assert/strict"; -import { z } from "zod"; +import { z } from "zod/v4"; import { Method, Middleware } from "express-zod-api"; export const authMiddleware = new Middleware({ diff --git a/express-zod-api/package.json b/express-zod-api/package.json index c1df9edef..307cfd2e8 100644 --- a/express-zod-api/package.json +++ b/express-zod-api/package.json @@ -76,7 +76,7 @@ "express-fileupload": "^1.5.0", "http-errors": "^2.0.0", "typescript": "^5.1.3", - "zod": "^4.0.0-beta.20250505T012514" + "zod": "3.25.0-beta.20250518T002810" }, "peerDependenciesMeta": { "@types/compression": { diff --git a/express-zod-api/src/api-response.ts b/express-zod-api/src/api-response.ts index 0d43926a8..4f914e0ab 100644 --- a/express-zod-api/src/api-response.ts +++ b/express-zod-api/src/api-response.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const defaultStatusCodes = { positive: 200, diff --git a/express-zod-api/src/common-helpers.ts b/express-zod-api/src/common-helpers.ts index 991351170..d70c00569 100644 --- a/express-zod-api/src/common-helpers.ts +++ b/express-zod-api/src/common-helpers.ts @@ -3,10 +3,10 @@ import type { $ZodTransform, $ZodType, $ZodTypeInternals, -} from "@zod/core"; +} from "zod/v4/core"; import { Request } from "express"; import * as R from "ramda"; -import { globalRegistry, z } from "zod"; +import { globalRegistry, z } from "zod/v4"; import { CommonConfig, InputSource, InputSources } from "./config-type"; import { contentTypes } from "./content-type"; import { OutputValidationError } from "./errors"; diff --git a/express-zod-api/src/date-in-schema.ts b/express-zod-api/src/date-in-schema.ts index 2222c80c1..26ecff8e9 100644 --- a/express-zod-api/src/date-in-schema.ts +++ b/express-zod-api/src/date-in-schema.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const ezDateInBrand = Symbol("DateIn"); diff --git a/express-zod-api/src/date-out-schema.ts b/express-zod-api/src/date-out-schema.ts index 6ab5e3e69..8f19d20c2 100644 --- a/express-zod-api/src/date-out-schema.ts +++ b/express-zod-api/src/date-out-schema.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const ezDateOutBrand = Symbol("DateOut"); diff --git a/express-zod-api/src/deep-checks.ts b/express-zod-api/src/deep-checks.ts index 36e442ee4..a1fec9e39 100644 --- a/express-zod-api/src/deep-checks.ts +++ b/express-zod-api/src/deep-checks.ts @@ -1,6 +1,6 @@ -import type { $ZodType, JSONSchema } from "@zod/core"; +import type { $ZodType, JSONSchema } from "zod/v4/core"; import * as R from "ramda"; -import { globalRegistry, z } from "zod"; +import { globalRegistry, z } from "zod/v4"; import { ezDateInBrand } from "./date-in-schema"; import { ezDateOutBrand } from "./date-out-schema"; import { DeepCheckError } from "./errors"; diff --git a/express-zod-api/src/diagnostics.ts b/express-zod-api/src/diagnostics.ts index 024f4ffcf..a0580c2c3 100644 --- a/express-zod-api/src/diagnostics.ts +++ b/express-zod-api/src/diagnostics.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { responseVariants } from "./api-response"; import { FlatObject, getRoutePathParams } from "./common-helpers"; import { contentTypes } from "./content-type"; diff --git a/express-zod-api/src/documentation-helpers.ts b/express-zod-api/src/documentation-helpers.ts index dfd03fb23..1848a5b11 100644 --- a/express-zod-api/src/documentation-helpers.ts +++ b/express-zod-api/src/documentation-helpers.ts @@ -5,7 +5,7 @@ import type { $ZodTuple, $ZodType, JSONSchema, -} from "@zod/core"; +} from "zod/v4/core"; import { ExamplesObject, isReferenceObject, @@ -23,7 +23,7 @@ import { TagObject, } from "openapi3-ts/oas31"; import * as R from "ramda"; -import { globalRegistry, z } from "zod"; +import { globalRegistry, z } from "zod/v4"; import { ResponseVariant } from "./api-response"; import { FlatObject, @@ -45,7 +45,7 @@ import { ezDateOutBrand } from "./date-out-schema"; import { DocumentationError } from "./errors"; import { ezFileBrand } from "./file-schema"; import { IOSchema } from "./io-schema"; -import { flattenIO, unref } from "./json-schema-helpers"; +import { flattenIO } from "./json-schema-helpers"; import { Alternatives } from "./logical-container"; import { metaSymbol } from "./metadata"; import { Method } from "./method"; @@ -463,7 +463,6 @@ const depict = ( unrepresentable: "any", io: ctx.isResponse ? "output" : "input", override: (zodCtx) => { - unref(zodCtx.jsonSchema); const { brand } = globalRegistry.get(zodCtx.zodSchema)?.[metaSymbol] ?? {}; const depicter = diff --git a/express-zod-api/src/endpoint.ts b/express-zod-api/src/endpoint.ts index b30db131f..7d3692fa7 100644 --- a/express-zod-api/src/endpoint.ts +++ b/express-zod-api/src/endpoint.ts @@ -1,6 +1,6 @@ import { Request, Response } from "express"; import * as R from "ramda"; -import { globalRegistry, z } from "zod"; +import { globalRegistry, z } from "zod/v4"; import { NormalizedResponse, ResponseVariant } from "./api-response"; import { findRequestTypeDefiningSchema } from "./deep-checks"; import { diff --git a/express-zod-api/src/endpoints-factory.ts b/express-zod-api/src/endpoints-factory.ts index a88169324..0cd7654e9 100644 --- a/express-zod-api/src/endpoints-factory.ts +++ b/express-zod-api/src/endpoints-factory.ts @@ -1,5 +1,5 @@ import { Request, Response } from "express"; -import { z } from "zod"; +import { z } from "zod/v4"; import { EmptyObject, EmptySchema, FlatObject, Tag } from "./common-helpers"; import { Endpoint, Handler } from "./endpoint"; import { IOSchema, getFinalEndpointInputSchema } from "./io-schema"; diff --git a/express-zod-api/src/errors.ts b/express-zod-api/src/errors.ts index 4a34b205e..26aaaae2d 100644 --- a/express-zod-api/src/errors.ts +++ b/express-zod-api/src/errors.ts @@ -1,5 +1,5 @@ -import type { $ZodType } from "@zod/core"; -import { z } from "zod"; +import type { $ZodType } from "zod/v4/core"; +import { z } from "zod/v4"; import { getMessageFromError } from "./common-helpers"; import { OpenAPIContext } from "./documentation-helpers"; import type { Method } from "./method"; diff --git a/express-zod-api/src/file-schema.ts b/express-zod-api/src/file-schema.ts index 7b9cd5956..bfbeab196 100644 --- a/express-zod-api/src/file-schema.ts +++ b/express-zod-api/src/file-schema.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const ezFileBrand = Symbol("File"); diff --git a/express-zod-api/src/form-schema.ts b/express-zod-api/src/form-schema.ts index ba19a9cbb..2a482dcdd 100644 --- a/express-zod-api/src/form-schema.ts +++ b/express-zod-api/src/form-schema.ts @@ -1,5 +1,5 @@ -import { z } from "zod"; -import type { $ZodShape } from "@zod/core"; +import { z } from "zod/v4"; +import type { $ZodShape } from "zod/v4/core"; export const ezFormBrand = Symbol("Form"); diff --git a/express-zod-api/src/integration.ts b/express-zod-api/src/integration.ts index 8910163a5..e74e6b164 100644 --- a/express-zod-api/src/integration.ts +++ b/express-zod-api/src/integration.ts @@ -1,6 +1,6 @@ import * as R from "ramda"; import ts from "typescript"; -import { z } from "zod"; +import { z } from "zod/v4"; import { ResponseVariant, responseVariants } from "./api-response"; import { IntegrationBase } from "./integration-base"; import { diff --git a/express-zod-api/src/io-schema.ts b/express-zod-api/src/io-schema.ts index ea8160b51..e56e9ab89 100644 --- a/express-zod-api/src/io-schema.ts +++ b/express-zod-api/src/io-schema.ts @@ -1,5 +1,5 @@ import * as R from "ramda"; -import { z } from "zod"; +import { z } from "zod/v4"; import { mixExamples } from "./metadata"; import { AbstractMiddleware } from "./middleware"; diff --git a/express-zod-api/src/json-schema-helpers.ts b/express-zod-api/src/json-schema-helpers.ts index 3aeaf3219..06dfda115 100644 --- a/express-zod-api/src/json-schema-helpers.ts +++ b/express-zod-api/src/json-schema-helpers.ts @@ -1,19 +1,7 @@ -import type { JSONSchema } from "@zod/core"; +import type { JSONSchema } from "zod/v4/core"; import * as R from "ramda"; import { combinations, isObject } from "./common-helpers"; -/** @link https://github.com/colinhacks/zod/issues/4275 */ -export const unref = ( - subject: JSONSchema.BaseSchema, -): Omit => { - while (subject._ref) { - const copy = { ...subject._ref }; - delete subject._ref; - Object.assign(subject, copy); - } - return subject; -}; - const isJsonObjectSchema = ( subject: JSONSchema.BaseSchema, ): subject is JSONSchema.ObjectSchema => subject.type === "object"; @@ -36,7 +24,7 @@ const canMerge = R.pipe( R.isEmpty, ); -const nestOptional = R.pipe(unref, R.pair(true)); +const nestOptional = R.pair(true); export const flattenIO = ( jsonSchema: JSONSchema.BaseSchema, @@ -54,8 +42,8 @@ export const flattenIO = ( if (entry.description) flat.description ??= entry.description; if (entry.allOf) { stack.push( - ...entry.allOf.map(unref).map((one) => { - if (mode === "throw" && !(one.type == "object" && canMerge(one))) + ...entry.allOf.map((one) => { + if (mode === "throw" && !(one.type === "object" && canMerge(one))) throw new Error("Can not merge"); return R.pair(isOptional, one); }), diff --git a/express-zod-api/src/metadata.ts b/express-zod-api/src/metadata.ts index 5f06dde24..4df62780f 100644 --- a/express-zod-api/src/metadata.ts +++ b/express-zod-api/src/metadata.ts @@ -1,5 +1,5 @@ import { combinations } from "./common-helpers"; -import { z } from "zod"; +import { z } from "zod/v4"; import * as R from "ramda"; export const metaSymbol = Symbol.for("express-zod-api"); diff --git a/express-zod-api/src/middleware.ts b/express-zod-api/src/middleware.ts index d8afb140b..ec0614b45 100644 --- a/express-zod-api/src/middleware.ts +++ b/express-zod-api/src/middleware.ts @@ -1,5 +1,5 @@ import { NextFunction, Request, Response } from "express"; -import { z } from "zod"; +import { z } from "zod/v4"; import { EmptySchema, FlatObject } from "./common-helpers"; import { InputValidationError } from "./errors"; import { IOSchema } from "./io-schema"; diff --git a/express-zod-api/src/migration.ts b/express-zod-api/src/migration.ts index c7e614b65..721aab09e 100644 --- a/express-zod-api/src/migration.ts +++ b/express-zod-api/src/migration.ts @@ -14,6 +14,7 @@ interface Queries { optionalPropStyle: NamedProp; depicter: TSESTree.ArrowFunctionExpression; nextCall: TSESTree.CallExpression; + zod: TSESTree.ImportDeclaration; } type Listener = keyof Queries; @@ -31,6 +32,7 @@ const queries: Record = { nextCall: `${NT.VariableDeclarator}[id.typeAnnotation.typeAnnotation.typeName.name='Depicter'] > ` + `${NT.ArrowFunctionExpression} ${NT.CallExpression}[callee.name='next']`, + zod: `${NT.ImportDeclaration}[source.value='zod']`, }; const listen = < @@ -120,6 +122,13 @@ const v24 = ESLintUtils.RuleCreator.withoutDocs({ data: { subject: "statement", from: "next()", to: "jsonSchema" }, fix: (fixer) => fixer.replaceText(node, "jsonSchema"), }), + zod: (node) => + ctx.report({ + node: node.source, + messageId: "change", + data: { subject: "import", from: "zod", to: "zod/v4" }, + fix: (fixer) => fixer.replaceText(node.source, `"zod/v4"`), + }), }), }); diff --git a/express-zod-api/src/raw-schema.ts b/express-zod-api/src/raw-schema.ts index 5becaf798..98b682dad 100644 --- a/express-zod-api/src/raw-schema.ts +++ b/express-zod-api/src/raw-schema.ts @@ -1,5 +1,5 @@ -import { z } from "zod"; -import type { $ZodShape } from "@zod/core"; +import { z } from "zod/v4"; +import type { $ZodShape } from "zod/v4/core"; import { file } from "./file-schema"; export const ezRawBrand = Symbol("Raw"); diff --git a/express-zod-api/src/result-handler.ts b/express-zod-api/src/result-handler.ts index fcf5ccfae..930308c3c 100644 --- a/express-zod-api/src/result-handler.ts +++ b/express-zod-api/src/result-handler.ts @@ -1,5 +1,5 @@ import { Request, Response } from "express"; -import { z } from "zod"; +import { z } from "zod/v4"; import { ApiResponse, defaultStatusCodes, diff --git a/express-zod-api/src/result-helpers.ts b/express-zod-api/src/result-helpers.ts index ff686a41b..cd69d8e83 100644 --- a/express-zod-api/src/result-helpers.ts +++ b/express-zod-api/src/result-helpers.ts @@ -1,6 +1,6 @@ import { Request } from "express"; import createHttpError, { HttpError, isHttpError } from "http-errors"; -import { z } from "zod"; +import { z } from "zod/v4"; import { NormalizedResponse, ResponseVariant } from "./api-response"; import { FlatObject, diff --git a/express-zod-api/src/schema-walker.ts b/express-zod-api/src/schema-walker.ts index 3ba4535fe..1c726fb27 100644 --- a/express-zod-api/src/schema-walker.ts +++ b/express-zod-api/src/schema-walker.ts @@ -1,5 +1,5 @@ -import type { $ZodType, $ZodTypeDef } from "@zod/core"; -import { globalRegistry } from "zod"; +import type { $ZodType, $ZodTypeDef } from "zod/v4/core"; +import { globalRegistry } from "zod/v4"; import type { EmptyObject, FlatObject } from "./common-helpers"; import { metaSymbol } from "./metadata"; diff --git a/express-zod-api/src/sse.ts b/express-zod-api/src/sse.ts index 45a1da93f..587967fe3 100644 --- a/express-zod-api/src/sse.ts +++ b/express-zod-api/src/sse.ts @@ -1,5 +1,5 @@ import { Response } from "express"; -import { z } from "zod"; +import { z } from "zod/v4"; import { EmptySchema, FlatObject } from "./common-helpers"; import { contentTypes } from "./content-type"; import { EndpointsFactory } from "./endpoints-factory"; diff --git a/express-zod-api/src/upload-schema.ts b/express-zod-api/src/upload-schema.ts index e6e5d634d..0ff633013 100644 --- a/express-zod-api/src/upload-schema.ts +++ b/express-zod-api/src/upload-schema.ts @@ -1,5 +1,5 @@ import type { UploadedFile } from "express-fileupload"; -import { z } from "zod"; +import { z } from "zod/v4"; export const ezUploadBrand = Symbol("Upload"); diff --git a/express-zod-api/src/zod-plugin.ts b/express-zod-api/src/zod-plugin.ts index ce0e7e8f3..43f6ca956 100644 --- a/express-zod-api/src/zod-plugin.ts +++ b/express-zod-api/src/zod-plugin.ts @@ -9,20 +9,25 @@ * @desc Ensures that the brand withstands additional refinements or checks * */ import * as R from "ramda"; -import { z, globalRegistry } from "zod"; +import { z, globalRegistry } from "zod/v4"; import { FlatObject } from "./common-helpers"; import { Metadata, metaSymbol } from "./metadata"; import { Intact, Remap } from "./mapping-helpers"; -import type { $ZodType, $ZodShape, $ZodLooseShape } from "@zod/core"; +import type { + $ZodType, + $ZodShape, + $ZodLooseShape, + $ZodObjectConfig, +} from "zod/v4/core"; -declare module "@zod/core" { +declare module "zod/v4/core" { interface GlobalMeta { [metaSymbol]?: Metadata; deprecated?: boolean; } } -declare module "zod" { +declare module "zod/v4" { interface ZodType { /** @desc Add an example value (before any transformations, can be called multiple times) */ example(example: z.input): this; @@ -35,8 +40,7 @@ declare module "zod" { interface ZodObject< // @ts-expect-error -- external issue out Shape extends $ZodShape = $ZodLooseShape, - OutExtra extends Record = Record, - InExtra extends Record = Record, + out Config extends $ZodObjectConfig = $ZodObjectConfig, > extends ZodType { remap( mapping: U, @@ -45,7 +49,7 @@ declare module "zod" { this, z.ZodTransform // internal type simplified >, - z.ZodObject & Intact, OutExtra, InExtra> + z.ZodObject & Intact, Config> >; remap( mapper: (subject: Shape) => U, @@ -108,7 +112,9 @@ const objectMapper = function ( R.map(([key, value]) => R.pair(tool[String(key)] || key, value)), R.fromPairs, ); - const nextShape = transformer(R.clone(this._zod.def.shape)); // immutable + const nextShape = transformer( + R.map(R.invoker(0, "clone"), this._zod.def.shape), // immutable, changed from R.clone due to failure + ); const hasPassThrough = this._zod.def.catchall instanceof z.ZodUnknown; const output = (hasPassThrough ? z.looseObject : z.object)(nextShape); // proxies unknown keys when set to "passthrough" return this.transform(transformer).pipe(output); diff --git a/express-zod-api/src/zts.ts b/express-zod-api/src/zts.ts index cc890007d..ba5827551 100644 --- a/express-zod-api/src/zts.ts +++ b/express-zod-api/src/zts.ts @@ -17,10 +17,10 @@ import type { $ZodTransform, $ZodTuple, $ZodUnion, -} from "@zod/core"; +} from "zod/v4/core"; import * as R from "ramda"; import ts from "typescript"; -import { globalRegistry, z } from "zod"; +import { globalRegistry, z } from "zod/v4"; import { getTransformedType, isOptional, isSchema } from "./common-helpers"; import { ezDateInBrand } from "./date-in-schema"; import { ezDateOutBrand } from "./date-out-schema"; diff --git a/express-zod-api/tests/__snapshots__/documentation.spec.ts.snap b/express-zod-api/tests/__snapshots__/documentation.spec.ts.snap index f789b9d7f..0e723377e 100644 --- a/express-zod-api/tests/__snapshots__/documentation.spec.ts.snap +++ b/express-zod-api/tests/__snapshots__/documentation.spec.ts.snap @@ -632,7 +632,7 @@ paths: items: type: integer exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 + maximum: 9007199254740991 - name: unlimited in: query required: true @@ -977,9 +977,9 @@ paths: type: - integer - "null" - exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 default: 123 + exclusiveMinimum: 0 + maximum: 9007199254740991 responses: "200": description: GET /v1/getSomething Positive response @@ -1091,7 +1091,7 @@ paths: two: type: integer exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 + maximum: 9007199254740991 required: - one - two @@ -1099,7 +1099,7 @@ paths: properties: two: type: integer - exclusiveMinimum: -9007199254740991 + minimum: -9007199254740991 exclusiveMaximum: 0 three: type: string @@ -1128,7 +1128,7 @@ paths: - type: string - type: integer exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 + maximum: 9007199254740991 required: - or required: @@ -1474,10 +1474,10 @@ paths: intPositive: type: integer exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 + maximum: 9007199254740991 intNegative: type: integer - exclusiveMinimum: -9007199254740991 + minimum: -9007199254740991 exclusiveMaximum: 0 intLimited: type: integer @@ -1955,7 +1955,7 @@ paths: two: type: integer exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 + maximum: 9007199254740991 required: - one - two @@ -2061,7 +2061,7 @@ paths: - type: string - type: integer exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 + maximum: 9007199254740991 items: not: {} required: @@ -3861,7 +3861,7 @@ paths: description: some positive integer type: integer exclusiveMinimum: 0 - exclusiveMaximum: 9007199254740991 + maximum: 9007199254740991 required: - result required: diff --git a/express-zod-api/tests/__snapshots__/endpoint.spec.ts.snap b/express-zod-api/tests/__snapshots__/endpoint.spec.ts.snap index 667dbd891..3d945f20c 100644 --- a/express-zod-api/tests/__snapshots__/endpoint.spec.ts.snap +++ b/express-zod-api/tests/__snapshots__/endpoint.spec.ts.snap @@ -21,6 +21,7 @@ exports[`Endpoint > .getResponses() > should return the negative responses (read "application/json", ], "schema": { + "$schema": "https://json-schema.org/draft-2020-12/schema", "properties": { "error": { "properties": { @@ -57,6 +58,7 @@ exports[`Endpoint > .getResponses() > should return the positive responses (read "application/json", ], "schema": { + "$schema": "https://json-schema.org/draft-2020-12/schema", "properties": { "data": { "properties": { diff --git a/express-zod-api/tests/__snapshots__/endpoints-factory.spec.ts.snap b/express-zod-api/tests/__snapshots__/endpoints-factory.spec.ts.snap index 694f854dd..e34c9bf4d 100644 --- a/express-zod-api/tests/__snapshots__/endpoints-factory.spec.ts.snap +++ b/express-zod-api/tests/__snapshots__/endpoints-factory.spec.ts.snap @@ -2,6 +2,7 @@ exports[`EndpointsFactory > .build() > Should create an endpoint with intersection middleware 1`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "allOf": [ { "allOf": [ @@ -46,6 +47,7 @@ exports[`EndpointsFactory > .build() > Should create an endpoint with intersecti exports[`EndpointsFactory > .build() > Should create an endpoint with intersection middleware 2`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "properties": { "b": { "type": "boolean", @@ -60,6 +62,7 @@ exports[`EndpointsFactory > .build() > Should create an endpoint with intersecti exports[`EndpointsFactory > .build() > Should create an endpoint with refined object middleware 1`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "allOf": [ { "properties": { @@ -90,6 +93,7 @@ exports[`EndpointsFactory > .build() > Should create an endpoint with refined ob exports[`EndpointsFactory > .build() > Should create an endpoint with refined object middleware 2`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "properties": { "o": { "type": "boolean", @@ -104,6 +108,7 @@ exports[`EndpointsFactory > .build() > Should create an endpoint with refined ob exports[`EndpointsFactory > .build() > Should create an endpoint with simple middleware 1`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "allOf": [ { "properties": { @@ -133,6 +138,7 @@ exports[`EndpointsFactory > .build() > Should create an endpoint with simple mid exports[`EndpointsFactory > .build() > Should create an endpoint with simple middleware 2`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "properties": { "b": { "type": "boolean", @@ -147,6 +153,7 @@ exports[`EndpointsFactory > .build() > Should create an endpoint with simple mid exports[`EndpointsFactory > .build() > Should create an endpoint with union middleware 1`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "allOf": [ { "anyOf": [ @@ -191,6 +198,7 @@ exports[`EndpointsFactory > .build() > Should create an endpoint with union midd exports[`EndpointsFactory > .build() > Should create an endpoint with union middleware 2`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "properties": { "b": { "type": "boolean", @@ -205,6 +213,7 @@ exports[`EndpointsFactory > .build() > Should create an endpoint with union midd exports[`EndpointsFactory > .buildVoid() > Should be a shorthand for empty object output 1`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "properties": {}, "required": [], "type": "object", diff --git a/express-zod-api/tests/__snapshots__/env.spec.ts.snap b/express-zod-api/tests/__snapshots__/env.spec.ts.snap index 71ea7bcfa..386fe6914 100644 --- a/express-zod-api/tests/__snapshots__/env.spec.ts.snap +++ b/express-zod-api/tests/__snapshots__/env.spec.ts.snap @@ -2,11 +2,11 @@ exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodEmail' definition 1`] = ` { - "check": [Function], - "computed": { + "bag": { "format": "email", "pattern": /\\^\\(\\?!\\\\\\.\\)\\(\\?!\\.\\*\\\\\\.\\\\\\.\\)\\(\\[A-Za-z0-9_'\\+\\\\-\\\\\\.\\]\\*\\)\\[A-Za-z0-9_\\+-\\]@\\(\\[A-Za-z0-9\\]\\[A-Za-z0-9\\\\-\\]\\*\\\\\\.\\)\\+\\[A-Za-z\\]\\{2,\\}\\$/, }, + "check": [Function], "constr": [Function], "def": { "abort": false, @@ -40,9 +40,8 @@ exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodEmai exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodNumber' definition 1`] = ` { - "computed": { + "bag": { "format": "safeint", - "inclusive": true, "maximum": 9007199254740991, "minimum": -9007199254740991, "pattern": /\\^\\\\d\\+\\$/, @@ -51,6 +50,7 @@ exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodNumb "def": { "checks": [ { + "$schema": "https://json-schema.org/draft-2020-12/schema", "maximum": 9007199254740991, "minimum": -9007199254740991, "type": "integer", @@ -73,14 +73,13 @@ exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodNumb exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodNumberFormat' definition 1`] = ` { - "check": [Function], - "computed": { + "bag": { "format": "safeint", - "inclusive": true, "maximum": 9007199254740991, "minimum": -9007199254740991, "pattern": /\\^\\\\d\\+\\$/, }, + "check": [Function], "constr": [Function], "def": { "abort": false, @@ -109,14 +108,13 @@ exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodNumb exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodNumberFormat' definition 2`] = ` { - "check": [Function], - "computed": { + "bag": { "format": "int32", - "inclusive": true, "maximum": 2147483647, "minimum": -2147483648, "pattern": /\\^\\\\d\\+\\$/, }, + "check": [Function], "constr": [Function], "def": { "abort": false, @@ -145,38 +143,19 @@ exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodNumb exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodNumberFormat' definition 3`] = ` { - "check": [Function], - "computed": { + "bag": { "format": "safeint", - "inclusive": true, "maximum": 1000, "minimum": -9007199254740991, "pattern": /\\^\\\\d\\+\\$/, }, + "check": [Function], "constr": [Function], "def": { "abort": false, "check": "number_format", "checks": [ - $ZodCheckLessThan { - "_zod": { - "check": [Function], - "constr": [Function], - "def": { - "check": "less_than", - "inclusive": true, - "value": 1000, - }, - "deferred": [], - "onattach": [ - [Function], - ], - "traits": Set { - "$ZodCheckLessThan", - "$ZodCheck", - }, - }, - }, + $ZodCheckLessThan {}, ], "format": "safeint", "type": "number", @@ -202,7 +181,7 @@ exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodNumb exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodString' definition 1`] = ` { - "computed": { + "bag": { "format": "email", "pattern": /\\^\\(\\?!\\\\\\.\\)\\(\\?!\\.\\*\\\\\\.\\\\\\.\\)\\(\\[A-Za-z0-9_'\\+\\\\-\\\\\\.\\]\\*\\)\\[A-Za-z0-9_\\+-\\]@\\(\\[A-Za-z0-9\\]\\[A-Za-z0-9\\\\-\\]\\*\\\\\\.\\)\\+\\[A-Za-z\\]\\{2,\\}\\$/, }, @@ -210,6 +189,7 @@ exports[`Environment checks > Zod checks/refinements > Snapshot control 'ZodStri "def": { "checks": [ { + "$schema": "https://json-schema.org/draft-2020-12/schema", "format": "email", "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$", "type": "string", @@ -237,6 +217,7 @@ exports[`Environment checks > Zod imperfections > circular object schema has no "configurable": true, "enumerable": true, "value": { + "$schema": "https://json-schema.org/draft-2020-12/schema", "items": { "properties": { "features": { @@ -260,6 +241,7 @@ exports[`Environment checks > Zod imperfections > circular object schema has no "configurable": true, "enumerable": true, "value": { + "$schema": "https://json-schema.org/draft-2020-12/schema", "type": "string", }, "writable": true, @@ -267,23 +249,25 @@ exports[`Environment checks > Zod imperfections > circular object schema has no } `; -exports[`Environment checks > Zod imperfections > input examples of transformations 1`] = ` +exports[`Environment checks > Zod imperfections > meta overrides, does not merge 1`] = ` { - "examples": [ - 4, - ], - "type": "string", + "title": "last", } `; -exports[`Environment checks > Zod imperfections > meta overrides, does not merge 1`] = ` +exports[`Environment checks > Zod new features > input examples of transformations 1`] = ` { - "title": "last", + "$schema": "https://json-schema.org/draft-2020-12/schema", + "examples": [ + "test", + ], + "type": "string", } `; -exports[`Environment checks > Zod imperfections > output examples of transformations 1`] = ` +exports[`Environment checks > Zod new features > output examples of transformations 1`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "examples": [ 4, ], diff --git a/express-zod-api/tests/__snapshots__/file-schema.spec.ts.snap b/express-zod-api/tests/__snapshots__/file-schema.spec.ts.snap index af5811b91..b1c8171d6 100644 --- a/express-zod-api/tests/__snapshots__/file-schema.spec.ts.snap +++ b/express-zod-api/tests/__snapshots__/file-schema.spec.ts.snap @@ -6,9 +6,7 @@ exports[`ez.file() > parsing > should perform additional check for base64 file 1 "code": "invalid_format", "format": "base64", "message": "Invalid base64-encoded string", - "origin": "string", "path": [], - "pattern": "/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/", }, ] `; diff --git a/express-zod-api/tests/__snapshots__/form-schema.spec.ts.snap b/express-zod-api/tests/__snapshots__/form-schema.spec.ts.snap index 3a0d6a9dd..54d144473 100644 --- a/express-zod-api/tests/__snapshots__/form-schema.spec.ts.snap +++ b/express-zod-api/tests/__snapshots__/form-schema.spec.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`ez.form() > parsing > should throw for missing props as a regular object schema 1`] = ` -ZodError { +ZodError({ "issues": [ { "code": "invalid_type", @@ -12,5 +12,5 @@ ZodError { ], }, ], -} +}) `; diff --git a/express-zod-api/tests/__snapshots__/io-schema.spec.ts.snap b/express-zod-api/tests/__snapshots__/io-schema.spec.ts.snap index 16bdb933c..45022ce85 100644 --- a/express-zod-api/tests/__snapshots__/io-schema.spec.ts.snap +++ b/express-zod-api/tests/__snapshots__/io-schema.spec.ts.snap @@ -2,6 +2,7 @@ exports[`I/O Schema and related helpers > getFinalEndpointInputSchema() > Should handle no middlewares 1`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "properties": { "four": { "type": "boolean", @@ -16,6 +17,7 @@ exports[`I/O Schema and related helpers > getFinalEndpointInputSchema() > Should exports[`I/O Schema and related helpers > getFinalEndpointInputSchema() > Should merge input object schemas 1`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "allOf": [ { "allOf": [ @@ -75,6 +77,7 @@ exports[`I/O Schema and related helpers > getFinalEndpointInputSchema() > Should exports[`I/O Schema and related helpers > getFinalEndpointInputSchema() > Should merge intersection object schemas 1`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "allOf": [ { "allOf": [ @@ -164,6 +167,7 @@ exports[`I/O Schema and related helpers > getFinalEndpointInputSchema() > Should exports[`I/O Schema and related helpers > getFinalEndpointInputSchema() > Should merge mixed object schemas 1`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "allOf": [ { "allOf": [ @@ -238,6 +242,7 @@ exports[`I/O Schema and related helpers > getFinalEndpointInputSchema() > Should exports[`I/O Schema and related helpers > getFinalEndpointInputSchema() > Should merge union object schemas 1`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "allOf": [ { "allOf": [ diff --git a/express-zod-api/tests/__snapshots__/sse.spec.ts.snap b/express-zod-api/tests/__snapshots__/sse.spec.ts.snap index ed0163083..18c5d84ff 100644 --- a/express-zod-api/tests/__snapshots__/sse.spec.ts.snap +++ b/express-zod-api/tests/__snapshots__/sse.spec.ts.snap @@ -2,6 +2,7 @@ exports[`SSE > makeEventSchema() > should make a valid schema of SSE event 1`] = ` { + "$schema": "https://json-schema.org/draft-2020-12/schema", "properties": { "data": { "type": "string", @@ -13,8 +14,8 @@ exports[`SSE > makeEventSchema() > should make a valid schema of SSE event 1`] = "type": "string", }, "retry": { - "exclusiveMaximum": 9007199254740991, "exclusiveMinimum": 0, + "maximum": 9007199254740991, "type": "integer", }, }, @@ -33,6 +34,7 @@ exports[`SSE > makeResultHandler() > should create ResultHandler describing poss "text/event-stream", ], "schema": { + "$schema": "https://json-schema.org/draft-2020-12/schema", "anyOf": [ { "properties": { @@ -46,8 +48,8 @@ exports[`SSE > makeResultHandler() > should create ResultHandler describing poss "type": "string", }, "retry": { - "exclusiveMaximum": 9007199254740991, "exclusiveMinimum": 0, + "maximum": 9007199254740991, "type": "integer", }, }, @@ -69,8 +71,8 @@ exports[`SSE > makeResultHandler() > should create ResultHandler describing poss "type": "string", }, "retry": { - "exclusiveMaximum": 9007199254740991, "exclusiveMinimum": 0, + "maximum": 9007199254740991, "type": "integer", }, }, @@ -96,6 +98,7 @@ exports[`SSE > makeResultHandler() > should create ResultHandler describing poss "text/plain", ], "schema": { + "$schema": "https://json-schema.org/draft-2020-12/schema", "type": "string", }, "statusCodes": [ @@ -112,6 +115,7 @@ exports[`SSE > makeResultHandler() > should create ResultHandler describing poss "text/event-stream", ], "schema": { + "$schema": "https://json-schema.org/draft-2020-12/schema", "properties": { "data": { "type": "string", @@ -123,8 +127,8 @@ exports[`SSE > makeResultHandler() > should create ResultHandler describing poss "type": "string", }, "retry": { - "exclusiveMaximum": 9007199254740991, "exclusiveMinimum": 0, + "maximum": 9007199254740991, "type": "integer", }, }, @@ -148,6 +152,7 @@ exports[`SSE > makeResultHandler() > should create ResultHandler describing poss "text/plain", ], "schema": { + "$schema": "https://json-schema.org/draft-2020-12/schema", "type": "string", }, "statusCodes": [ diff --git a/express-zod-api/tests/__snapshots__/system.spec.ts.snap b/express-zod-api/tests/__snapshots__/system.spec.ts.snap index c22eb5407..19d959f79 100644 --- a/express-zod-api/tests/__snapshots__/system.spec.ts.snap +++ b/express-zod-api/tests/__snapshots__/system.spec.ts.snap @@ -122,7 +122,7 @@ exports[`App in production mode > Validation > Problem 787: Should NOT treat Zod "Server side error", { "error": InternalServerError({ - "cause": ZodError { + "cause": ZodError({ "issues": [ { "code": "invalid_type", @@ -131,7 +131,7 @@ exports[`App in production mode > Validation > Problem 787: Should NOT treat Zod "path": [], }, ], - }, + }), "message": "Invalid input: expected number, received string", }), "payload": { @@ -166,7 +166,7 @@ exports[`App in production mode > Validation > Should fail on handler output typ "Server side error", { "error": InternalServerError({ - "cause": ZodError { + "cause": ZodError({ "issues": [ { "code": "too_small", @@ -179,7 +179,7 @@ exports[`App in production mode > Validation > Should fail on handler output typ ], }, ], - }, + }), "message": "output/anything: Too small: expected number to be >0", }), "payload": { diff --git a/express-zod-api/tests/common-helpers.spec.ts b/express-zod-api/tests/common-helpers.spec.ts index d697b3bca..48508be52 100644 --- a/express-zod-api/tests/common-helpers.spec.ts +++ b/express-zod-api/tests/common-helpers.spec.ts @@ -10,7 +10,7 @@ import { pullExampleProps, getRoutePathParams, } from "../src/common-helpers"; -import { z } from "zod"; +import { z } from "zod/v4"; import { makeRequestMock } from "../src/testing"; describe("Common Helpers", () => { diff --git a/express-zod-api/tests/date-in-schema.spec.ts b/express-zod-api/tests/date-in-schema.spec.ts index 60384c1ae..432dc8b08 100644 --- a/express-zod-api/tests/date-in-schema.spec.ts +++ b/express-zod-api/tests/date-in-schema.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { ezDateInBrand } from "../src/date-in-schema"; import { ez } from "../src"; import { metaSymbol } from "../src/metadata"; diff --git a/express-zod-api/tests/date-out-schema.spec.ts b/express-zod-api/tests/date-out-schema.spec.ts index 25dd350a4..0ad0b0eb7 100644 --- a/express-zod-api/tests/date-out-schema.spec.ts +++ b/express-zod-api/tests/date-out-schema.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { ezDateOutBrand } from "../src/date-out-schema"; import { ez } from "../src"; import { metaSymbol } from "../src/metadata"; diff --git a/express-zod-api/tests/deep-checks.spec.ts b/express-zod-api/tests/deep-checks.spec.ts index f4695711e..5819791ce 100644 --- a/express-zod-api/tests/deep-checks.spec.ts +++ b/express-zod-api/tests/deep-checks.spec.ts @@ -1,6 +1,6 @@ import { UploadedFile } from "express-fileupload"; -import { globalRegistry, z } from "zod"; -import type { $brand, $ZodType } from "@zod/core"; +import { globalRegistry, z } from "zod/v4"; +import type { $brand, $ZodType } from "zod/v4/core"; import { ez } from "../src"; import { findNestedSchema, hasCycle } from "../src/deep-checks"; import { metaSymbol } from "../src/metadata"; diff --git a/express-zod-api/tests/depends-on-method.spec.ts b/express-zod-api/tests/depends-on-method.spec.ts index 0b1f862a7..cf9841c8f 100644 --- a/express-zod-api/tests/depends-on-method.spec.ts +++ b/express-zod-api/tests/depends-on-method.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { DependsOnMethod, EndpointsFactory, diff --git a/express-zod-api/tests/documentation-helpers.spec.ts b/express-zod-api/tests/documentation-helpers.spec.ts index 90a3c6d9f..89b679eb3 100644 --- a/express-zod-api/tests/documentation-helpers.spec.ts +++ b/express-zod-api/tests/documentation-helpers.spec.ts @@ -1,7 +1,7 @@ -import { JSONSchema } from "@zod/core"; +import { JSONSchema } from "zod/v4/core"; import { SchemaObject } from "openapi3-ts/oas31"; import * as R from "ramda"; -import { z } from "zod"; +import { z } from "zod/v4"; import { ez } from "../src"; import { OpenAPIContext, diff --git a/express-zod-api/tests/documentation.spec.ts b/express-zod-api/tests/documentation.spec.ts index 00dc3d278..2241cb666 100644 --- a/express-zod-api/tests/documentation.spec.ts +++ b/express-zod-api/tests/documentation.spec.ts @@ -12,7 +12,7 @@ import { Depicter, } from "../src"; import { contentTypes } from "../src/content-type"; -import { z } from "zod"; +import { z } from "zod/v4"; import { givePort } from "../../tools/ports"; describe("Documentation", () => { diff --git a/express-zod-api/tests/endpoint.spec.ts b/express-zod-api/tests/endpoint.spec.ts index 165d921e9..bb33e2602 100644 --- a/express-zod-api/tests/endpoint.spec.ts +++ b/express-zod-api/tests/endpoint.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { EndpointsFactory, Middleware, diff --git a/express-zod-api/tests/endpoints-factory.spec.ts b/express-zod-api/tests/endpoints-factory.spec.ts index 466993538..8780588b7 100644 --- a/express-zod-api/tests/endpoints-factory.spec.ts +++ b/express-zod-api/tests/endpoints-factory.spec.ts @@ -9,7 +9,7 @@ import { } from "../src"; import { EmptyObject, EmptySchema } from "../src/common-helpers"; import { Endpoint } from "../src/endpoint"; -import { z } from "zod"; +import { z } from "zod/v4"; describe("EndpointsFactory", () => { const resultHandlerMock = new ResultHandler({ diff --git a/express-zod-api/tests/env.spec.ts b/express-zod-api/tests/env.spec.ts index 9bea65916..6316390c8 100644 --- a/express-zod-api/tests/env.spec.ts +++ b/express-zod-api/tests/env.spec.ts @@ -1,6 +1,6 @@ import createHttpError from "http-errors"; import * as R from "ramda"; -import { z } from "zod"; +import { z } from "zod/v4"; describe("Environment checks", () => { describe("Zod Dates", () => { @@ -46,26 +46,10 @@ describe("Environment checks", () => { }); test("bigint is not representable", () => { - expect(z.toJSONSchema(z.bigint(), { unrepresentable: "any" })).toEqual( - {}, - ); + const json = z.toJSONSchema(z.bigint(), { unrepresentable: "any" }); + expect(R.omit(["$schema"], json)).toEqual({}); }); - /** @link https://github.com/colinhacks/zod/issues/4274 */ - test.each(["input", "output"] as const)( - "%s examples of transformations", - (io) => { - const schema = z - .string() - .meta({ examples: ["test"] }) - .transform(Number) - .meta({ examples: [4] }); - expect( - z.toJSONSchema(schema, { io, unrepresentable: "any" }), - ).toMatchSnapshot(); - }, - ); - test("meta overrides, does not merge", () => { const schema = z .string() @@ -75,18 +59,6 @@ describe("Environment checks", () => { expect(schema.meta()).toMatchSnapshot(); }); - /** @link https://github.com/colinhacks/zod/issues/4320 */ - test("input type of a loose object does not allow extra keys", () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars -- this is fine - const schema = z.looseObject({}); - expectTypeOf>().toEqualTypeOf< - Record // ok - >(); - expectTypeOf>().not.toEqualTypeOf< - Record // not ok - >(); - }); - test("circular object schema has no sign of getter in its shape", () => { const schema = z.object({ name: z.string(), @@ -98,6 +70,18 @@ describe("Environment checks", () => { Object.getOwnPropertyDescriptors(schema._zod.def.shape), ).toMatchSnapshot(); }); + + /** + * told Colin directly + * @todo adjust vitest.setup.ts on custom serialization if fixed + * */ + test("ZodError inequality", () => { + try { + z.number().parse("test"); + } catch (caught) { + expect(z.number().safeParse("test").error).not.toEqual(caught); + } + }); }); describe("Zod new features", () => { @@ -141,6 +125,24 @@ describe("Environment checks", () => { expect(boolSchema.isOptional()).toBeTruthy(); expect(boolSchema.isNullable()).toBeTruthy(); }); + + /** + * @link https://github.com/colinhacks/zod/issues/4274 + * @todo this fact can be used for switching to native examples + * */ + test.each(["input", "output"] as const)( + "%s examples of transformations", + (io) => { + const schema = z + .string() + .meta({ examples: ["test"] }) + .transform(Number) + .meta({ examples: [4] }); + expect( + z.toJSONSchema(schema, { io, unrepresentable: "any" }), + ).toMatchSnapshot(); + }, + ); }); describe("Vitest error comparison", () => { diff --git a/express-zod-api/tests/errors.spec.ts b/express-zod-api/tests/errors.spec.ts index 552978cde..6ab84605d 100644 --- a/express-zod-api/tests/errors.spec.ts +++ b/express-zod-api/tests/errors.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { DocumentationError, RoutingError } from "../src"; import { IOSchemaError, diff --git a/express-zod-api/tests/file-schema.spec.ts b/express-zod-api/tests/file-schema.spec.ts index 1c5e95c6b..c9c30b708 100644 --- a/express-zod-api/tests/file-schema.spec.ts +++ b/express-zod-api/tests/file-schema.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { ezFileBrand } from "../src/file-schema"; import { ez } from "../src"; import { readFile } from "node:fs/promises"; diff --git a/express-zod-api/tests/form-schema.spec.ts b/express-zod-api/tests/form-schema.spec.ts index de2aa3768..44e09d2d1 100644 --- a/express-zod-api/tests/form-schema.spec.ts +++ b/express-zod-api/tests/form-schema.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { ez } from "../src"; import { ezFormBrand } from "../src/form-schema"; import { metaSymbol } from "../src/metadata"; diff --git a/express-zod-api/tests/index.spec.ts b/express-zod-api/tests/index.spec.ts index 037a309c5..d835b7d07 100644 --- a/express-zod-api/tests/index.spec.ts +++ b/express-zod-api/tests/index.spec.ts @@ -1,8 +1,8 @@ -import type { $ZodType, JSONSchema } from "@zod/core"; +import type { $ZodType, JSONSchema } from "zod/v4/core"; import { IRouter } from "express"; import ts from "typescript"; import { expectTypeOf } from "vitest"; -import { z } from "zod"; +import { z } from "zod/v4"; import * as entrypoint from "../src"; import { ApiResponse, diff --git a/express-zod-api/tests/integration.spec.ts b/express-zod-api/tests/integration.spec.ts index ec9beb660..06349e4e3 100644 --- a/express-zod-api/tests/integration.spec.ts +++ b/express-zod-api/tests/integration.spec.ts @@ -1,5 +1,5 @@ import ts from "typescript"; -import { globalRegistry, z } from "zod"; +import { globalRegistry, z } from "zod/v4"; import { EndpointsFactory, Integration, diff --git a/express-zod-api/tests/io-schema.spec.ts b/express-zod-api/tests/io-schema.spec.ts index bf3259258..b53ebb601 100644 --- a/express-zod-api/tests/io-schema.spec.ts +++ b/express-zod-api/tests/io-schema.spec.ts @@ -1,5 +1,5 @@ import { expectTypeOf } from "vitest"; -import { z } from "zod"; +import { z } from "zod/v4"; import { IOSchema, Middleware, ez } from "../src"; import { getFinalEndpointInputSchema } from "../src/io-schema"; import { metaSymbol } from "../src/metadata"; @@ -119,7 +119,7 @@ describe("I/O Schema and related helpers", () => { expectTypeOf(z.object({}).transform(() => [])).not.toExtend(); }); test("does not accept piping into another kind of schema", () => { - expectTypeOf(z.unknown({}).pipe(z.string())).not.toExtend(); + expectTypeOf(z.unknown().pipe(z.string())).not.toExtend(); expectTypeOf( z .object({ s: z.string() }) diff --git a/express-zod-api/tests/json-schema-helpers.spec.ts b/express-zod-api/tests/json-schema-helpers.spec.ts index defeeed76..bca124739 100644 --- a/express-zod-api/tests/json-schema-helpers.spec.ts +++ b/express-zod-api/tests/json-schema-helpers.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { flattenIO } from "../src/json-schema-helpers"; describe("JSON Schema helpers", () => { diff --git a/express-zod-api/tests/metadata.spec.ts b/express-zod-api/tests/metadata.spec.ts index c099d0eac..f78820dd0 100644 --- a/express-zod-api/tests/metadata.spec.ts +++ b/express-zod-api/tests/metadata.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { mixExamples, metaSymbol } from "../src/metadata"; describe("Metadata", () => { diff --git a/express-zod-api/tests/middleware.spec.ts b/express-zod-api/tests/middleware.spec.ts index 0505f57da..bbebfb1bc 100644 --- a/express-zod-api/tests/middleware.spec.ts +++ b/express-zod-api/tests/middleware.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { InputValidationError, Middleware } from "../src"; import { EmptyObject } from "../src/common-helpers"; import { AbstractMiddleware, ExpressMiddleware } from "../src/middleware"; diff --git a/express-zod-api/tests/migration.spec.ts b/express-zod-api/tests/migration.spec.ts index 89862d9ca..17b259e46 100644 --- a/express-zod-api/tests/migration.spec.ts +++ b/express-zod-api/tests/migration.spec.ts @@ -20,8 +20,9 @@ describe("Migration", () => { tester.run("v24", migration.rules.v24, { valid: [ `new Documentation({});`, - `new Integration({})`, - `const rule: Depicter = () => {}`, + `new Integration({});`, + `const rule: Depicter = () => {};`, + `import {} from "zod/v4";`, ], invalid: [ { @@ -66,6 +67,16 @@ describe("Migration", () => { }, ], }, + { + code: `import {} from "zod";`, + output: `import {} from "zod/v4";`, + errors: [ + { + messageId: "change", + data: { subject: "import", from: "zod", to: "zod/v4" }, + }, + ], + }, ], }); }); diff --git a/express-zod-api/tests/raw-schema.spec.ts b/express-zod-api/tests/raw-schema.spec.ts index c308f54fb..a0b17cffc 100644 --- a/express-zod-api/tests/raw-schema.spec.ts +++ b/express-zod-api/tests/raw-schema.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { ez } from "../src"; import { metaSymbol } from "../src/metadata"; import { ezRawBrand } from "../src/raw-schema"; diff --git a/express-zod-api/tests/result-handler.spec.ts b/express-zod-api/tests/result-handler.spec.ts index 7fe11286a..7a22c8acc 100644 --- a/express-zod-api/tests/result-handler.spec.ts +++ b/express-zod-api/tests/result-handler.spec.ts @@ -1,6 +1,6 @@ import { Response } from "express"; import createHttpError from "http-errors"; -import { z } from "zod"; +import { z } from "zod/v4"; import { InputValidationError, arrayResultHandler, diff --git a/express-zod-api/tests/result-helpers.spec.ts b/express-zod-api/tests/result-helpers.spec.ts index 4aa6dbc77..fcaa08d25 100644 --- a/express-zod-api/tests/result-helpers.spec.ts +++ b/express-zod-api/tests/result-helpers.spec.ts @@ -1,5 +1,5 @@ import createHttpError from "http-errors"; -import { z } from "zod"; +import { z } from "zod/v4"; import { InputValidationError, OutputValidationError } from "../src"; import { ensureHttpError, diff --git a/express-zod-api/tests/routable.spec.ts b/express-zod-api/tests/routable.spec.ts index ac14ac457..0e3046670 100644 --- a/express-zod-api/tests/routable.spec.ts +++ b/express-zod-api/tests/routable.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { defaultEndpointsFactory, DependsOnMethod } from "../src"; const endpoint = defaultEndpointsFactory.build({ diff --git a/express-zod-api/tests/routing.spec.ts b/express-zod-api/tests/routing.spec.ts index 9a6b67f3c..3a8b50ff0 100644 --- a/express-zod-api/tests/routing.spec.ts +++ b/express-zod-api/tests/routing.spec.ts @@ -4,7 +4,7 @@ import { staticHandler, staticMock, } from "./express-mock"; -import { z } from "zod"; +import { z } from "zod/v4"; import { DependsOnMethod, EndpointsFactory, diff --git a/express-zod-api/tests/server.spec.ts b/express-zod-api/tests/server.spec.ts index 6d2c9bc01..cbf7a7b29 100644 --- a/express-zod-api/tests/server.spec.ts +++ b/express-zod-api/tests/server.spec.ts @@ -13,7 +13,7 @@ import { httpListenSpy, httpsListenSpy, } from "./http-mock"; -import { z } from "zod"; +import { z } from "zod/v4"; import { AppConfig, BuiltinLogger, diff --git a/express-zod-api/tests/sse.spec.ts b/express-zod-api/tests/sse.spec.ts index ef90eb00c..77f6b965d 100644 --- a/express-zod-api/tests/sse.spec.ts +++ b/express-zod-api/tests/sse.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { FlatObject, Middleware, diff --git a/express-zod-api/tests/system.spec.ts b/express-zod-api/tests/system.spec.ts index 7b52085e8..b6f4bcab1 100644 --- a/express-zod-api/tests/system.spec.ts +++ b/express-zod-api/tests/system.spec.ts @@ -2,7 +2,7 @@ import cors from "cors"; import depd from "depd"; import express from "express"; import { readFile } from "node:fs/promises"; -import { z } from "zod"; +import { z } from "zod/v4"; import { EndpointsFactory, Method, diff --git a/express-zod-api/tests/testing.spec.ts b/express-zod-api/tests/testing.spec.ts index 41e96b6ba..68e11768d 100644 --- a/express-zod-api/tests/testing.spec.ts +++ b/express-zod-api/tests/testing.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { CommonConfig, defaultEndpointsFactory, diff --git a/express-zod-api/tests/upload-schema.spec.ts b/express-zod-api/tests/upload-schema.spec.ts index eb58f093d..6177bcfc9 100644 --- a/express-zod-api/tests/upload-schema.spec.ts +++ b/express-zod-api/tests/upload-schema.spec.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { ez } from "../src"; import { metaSymbol } from "../src/metadata"; import { ezUploadBrand } from "../src/upload-schema"; diff --git a/express-zod-api/tests/zod-plugin.spec.ts b/express-zod-api/tests/zod-plugin.spec.ts index 587456728..fff0e3f07 100644 --- a/express-zod-api/tests/zod-plugin.spec.ts +++ b/express-zod-api/tests/zod-plugin.spec.ts @@ -1,5 +1,5 @@ import camelize from "camelize-ts"; -import { z } from "zod"; +import { z } from "zod/v4"; import { metaSymbol } from "../src/metadata"; describe("Zod Runtime Plugin", () => { @@ -102,9 +102,10 @@ describe("Zod Runtime Plugin", () => { const schema = z.object({ user_id: z.string() }); const mappedSchema = schema.remap({ user_id: "userId" }); expect(mappedSchema.in.in).toEqual(schema); - expect(mappedSchema.out.shape).toEqual({ - userId: schema.shape.user_id, - }); + expect(mappedSchema.out.shape).toHaveProperty( + "userId", + expect.any(z.ZodString), + ); expect(mappedSchema._zod.def.out.shape.userId).not.toBe( schema.shape.user_id, ); @@ -118,10 +119,14 @@ describe("Zod Runtime Plugin", () => { (mapping) => { const schema = z.object({ user_id: z.string(), name: z.string() }); const mappedSchema = schema.remap(mapping); - expect(mappedSchema._zod.def.out.shape).toEqual({ - userId: schema.shape.user_id, - name: schema.shape.name, - }); + expect(mappedSchema._zod.def.out.shape).toHaveProperty( + "userId", + expect.any(z.ZodString), + ); + expect(mappedSchema._zod.def.out.shape).toHaveProperty( + "name", + expect.any(z.ZodString), + ); expect(mappedSchema.parse({ user_id: "test", name: "some" })).toEqual({ userId: "test", name: "some", @@ -132,10 +137,14 @@ describe("Zod Runtime Plugin", () => { test("should support a mapping function", () => { const schema = z.object({ user_id: z.string(), name: z.string() }); const mappedSchema = schema.remap((shape) => camelize(shape, true)); - expect(mappedSchema._zod.def.out.shape).toEqual({ - userId: schema.shape.user_id, - name: schema.shape.name, - }); + expect(mappedSchema._zod.def.out.shape).toHaveProperty( + "userId", + expect.any(z.ZodString), + ); + expect(mappedSchema._zod.def.out.shape).toHaveProperty( + "name", + expect.any(z.ZodString), + ); expect(mappedSchema.parse({ user_id: "test", name: "some" })).toEqual({ userId: "test", name: "some", diff --git a/express-zod-api/tests/zts.spec.ts b/express-zod-api/tests/zts.spec.ts index e1649ac48..5a12c5e94 100644 --- a/express-zod-api/tests/zts.spec.ts +++ b/express-zod-api/tests/zts.spec.ts @@ -1,5 +1,5 @@ import ts from "typescript"; -import { z } from "zod"; +import { z } from "zod/v4"; import { ez } from "../src"; import { f, printNode } from "../src/typescript-api"; import { zodToTs } from "../src/zts"; diff --git a/express-zod-api/vitest.setup.ts b/express-zod-api/vitest.setup.ts index b7c867470..d52c1e218 100644 --- a/express-zod-api/vitest.setup.ts +++ b/express-zod-api/vitest.setup.ts @@ -1,6 +1,6 @@ import "./src/zod-plugin"; // required for tests importing sources using the plugin methods import type { NewPlugin } from "@vitest/pretty-format"; -import { globalRegistry, z } from "zod"; +import { globalRegistry, z } from "zod/v4"; import { ResultHandlerError } from "./src/errors"; import { metaSymbol } from "./src/metadata"; @@ -10,12 +10,16 @@ const errorSerializer: NewPlugin = { serialize: (error: Error, config, indentation, depth, refs, printer) => { const { name, message, cause } = error; const { handled } = error instanceof ResultHandlerError ? error : {}; + const { issues } = error instanceof z.ZodError ? error : {}; const obj = Object.assign( - { message }, + {}, + message && { message }, cause && { cause }, handled && { handled }, + issues && { issues }, ); - return `${name}(${printer(obj, config, indentation, depth, refs)})`; + // @todo external issue with ZodError.name + return `${issues ? "ZodError" : name}(${printer(obj, config, indentation, depth, refs)})`; }, }; diff --git a/package.json b/package.json index 4cdc490e7..c8481106f 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "typescript": "^5.8.3", "typescript-eslint": "^8.32.1", "vitest": "^3.1.3", - "zod": "^4.0.0-beta.20250505T195954" + "zod": "3.25.0-beta.20250518T002810" }, "resolutions": { "**/@scarf/scarf": "npm:empty-npm-package@1.0.0" diff --git a/yarn.lock b/yarn.lock index 07c5a1e14..4d0c0076f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -825,11 +825,6 @@ loupe "^3.1.3" tinyrainbow "^2.0.0" -"@zod/core@0.11.6": - version "0.11.6" - resolved "https://registry.yarnpkg.com/@zod/core/-/core-0.11.6.tgz#9216e98848dc9364eda35e3da90f5362f10e8887" - integrity sha512-03Bv82fFSfjDAvMfdHHdGSS6SOJs0iCcJlWJv1kJHRtoTT02hZpyip/2Lk6oo4l4FtjuwTrsEQTwg/LD8I7dJA== - accepts@^1.3.7: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -3128,9 +3123,7 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zod@^4.0.0-beta.20250505T195954: - version "4.0.0-beta.20250505T195954" - resolved "https://registry.yarnpkg.com/zod/-/zod-4.0.0-beta.20250505T195954.tgz#ba9da025671de2dde9d4d033089f03c37a35022f" - integrity sha512-iB8WvxkobVIXMARvQu20fKvbS7mUTiYRpcD8OQV1xjRhxO0EEpYIRJBk6yfBzHAHEdOSDh3SxDITr5Eajr2vtg== - dependencies: - "@zod/core" "0.11.6" +zod@3.25.0-beta.20250518T002810: + version "3.25.0-beta.20250518T002810" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.0-beta.20250518T002810.tgz#9ac105aea4b6e0d072a382893c3e6556670048c8" + integrity sha512-3/aIqMbUXG9EjTelJkDcWd+izJP5MxFgQEMSYI8n41pwYhRDYYxy2dnbkgfNcnLbFZ9uByZn9XXqHTh05QHqSQ==