Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
300ef10
chore(): upgrade zod to latest
adrien2p Dec 15, 2025
c8e1188
chore(): upgrade zod to latest
adrien2p Dec 15, 2025
8247f40
chore(): upgrade zod to latest
adrien2p Dec 15, 2025
ecc645c
Merge branch 'develop' into chore/upgrade-zod
adrien2p Dec 15, 2025
a734abd
chore(): upgrade zod to latest
adrien2p Dec 15, 2025
8b93f72
chore(): upgrade zod to latest
adrien2p Dec 15, 2025
95cc909
chore(): upgrade zod to latest
adrien2p Dec 15, 2025
0bd0f9b
chore(): upgrade zod to latest
adrien2p Dec 15, 2025
ad5aef4
force trigger cache miss
adrien2p Dec 15, 2025
838d727
fix deps
adrien2p Dec 15, 2025
b50471b
fix deps
adrien2p Dec 15, 2025
44fddb4
Merge branch 'develop' into chore/upgrade-zod
adrien2p Dec 15, 2025
3177cd1
fix
adrien2p Dec 16, 2025
e437560
Merge branch 'chore/upgrade-zod' of github.com:medusajs/medusa into c…
adrien2p Dec 16, 2025
0615c4c
fix tests
adrien2p Dec 16, 2025
7b36126
Merge branch 'develop' into chore/upgrade-zod
adrien2p Dec 16, 2025
0cb4bc8
fix tests
adrien2p Dec 16, 2025
5fb4404
fix tests
adrien2p Dec 16, 2025
e887a2b
fix tests
adrien2p Dec 16, 2025
85cc926
cleanup
adrien2p Dec 16, 2025
8016ad5
Merge branch 'develop' into chore/upgrade-zod
adrien2p Dec 16, 2025
2f3b692
fix tests
adrien2p Dec 16, 2025
9d0af82
Merge branch 'develop' into chore/upgrade-zod
adrien2p Dec 16, 2025
f82c231
Merge branch 'develop' into chore/upgrade-zod
adrien2p Dec 17, 2025
f2d38b1
resolve-conflicts
adrien2p Dec 17, 2025
4271112
Merge branch 'chore/upgrade-zod' of github.com:medusajs/medusa into c…
adrien2p Dec 17, 2025
2abe9ce
remove only
adrien2p Dec 17, 2025
2ce063a
Create fifty-boats-battle.md
adrien2p Dec 17, 2025
28335de
Merge branch 'develop' into chore/upgrade-zod
adrien2p Dec 17, 2025
0ee0d90
Merge branch 'develop' into chore/upgrade-zod
adrien2p Dec 18, 2025
5ccdc1d
reduce nullish coalescing
adrien2p Dec 18, 2025
0310e6b
Merge branch 'chore/upgrade-zod' of github.com:medusajs/medusa into c…
adrien2p Dec 18, 2025
140ca54
Merge branch 'develop' into chore/upgrade-zod
adrien2p Dec 18, 2025
eebe3d3
resolve conflicts
adrien2p Jan 5, 2026
b401c26
cleanup
adrien2p Jan 5, 2026
8842d38
cleanup
adrien2p Jan 5, 2026
c0564f4
cleanup + fixes
adrien2p Jan 5, 2026
9ef81f7
ordering import and type
adrien2p Jan 5, 2026
bfa19a1
improve error handler + fix tests and add new tests
adrien2p Jan 5, 2026
afccd42
improve test cases
adrien2p Jan 5, 2026
307e26a
Merge branch 'develop' into chore/upgrade-zod
adrien2p Jan 5, 2026
819c2fb
Merge branch 'develop' into chore/upgrade-zod
adrien2p Jan 6, 2026
49c68a6
resolve conflicts
adrien2p Jan 9, 2026
ce4f859
fix
adrien2p Jan 9, 2026
2f8ce89
Merge branch 'develop' into chore/upgrade-zod
adrien2p Jan 13, 2026
12faca9
Merge branch 'develop' into chore/upgrade-zod
adrien2p Jan 15, 2026
e937e5d
update import
adrien2p Jan 15, 2026
72f5d89
Merge branch 'develop' into chore/upgrade-zod
shahednasser Apr 10, 2026
903c076
update yarn.lock
shahednasser Apr 10, 2026
a51ef49
fix
shahednasser Apr 10, 2026
a5631a7
Merge branch 'develop' into chore/upgrade-zod
shahednasser Apr 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/fifty-boats-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@medusajs/medusa": patch
"@medusajs/core-flows": patch
"@medusajs/framework": patch
"@medusajs/utils": patch
"@medusajs/workflows-sdk": patch
"@medusajs/admin-sdk": patch
"@medusajs/dashboard": patch
---

chore(): upgrade zod to latest
Original file line number Diff line number Diff line change
Expand Up @@ -1146,10 +1146,9 @@ medusaIntegrationTestRunner({
issues: [
{
code: "invalid_type",
expected: "object",
message: "Expected object, received array",
expected: "record",
message: "Invalid input: expected record, received array",
path: [],
received: "array",
},
],
}),
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@
"vite-plugin-turbosnap": "^1.0.2",
"vitest": "^3.0.5",
"yalc": "^1.0.0-pre.53",
"zod": "3.25.76"
"zod": "4.2.0",
"zod-validation-error": "5.0.0"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": "yarn run lint",
Expand Down
2 changes: 1 addition & 1 deletion packages/admin/admin-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
},
"dependencies": {
"@medusajs/admin-shared": "2.13.6",
"zod": "3.25.76"
"zod": "4.2.0"
},
"packageManager": "yarn@3.2.1"
}
2 changes: 1 addition & 1 deletion packages/admin/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
"react-i18next": "13.5.0",
"react-jwt": "^1.2.0",
"react-router-dom": "6.30.3",
"zod": "3.25.76"
"zod": "4.2.0"
},
"devDependencies": {
"@medusajs/admin-shared": "2.13.6",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,4 @@
import {
ZodBoolean,
ZodEffects,
ZodNull,
ZodNullable,
ZodNumber,
ZodOptional,
ZodString,
ZodType,
ZodUndefined,
} from "zod"
import { z } from "zod"
import { FormFieldType } from "./types"

export function getFieldLabel(name: string, label?: string) {
Expand All @@ -22,45 +12,42 @@ export function getFieldLabel(name: string, label?: string) {
.join(" ")
}

export function getFieldType(type: ZodType): FormFieldType {
if (type instanceof ZodString) {
export function getFieldType(type: z.ZodType): FormFieldType {
if (type instanceof z.ZodString) {
return "text"
}

if (type instanceof ZodNumber) {
if (type instanceof z.ZodNumber) {
return "number"
}

if (type instanceof ZodBoolean) {
if (type instanceof z.ZodBoolean) {
return "boolean"
}

if (type instanceof ZodNullable) {
const innerType = type.unwrap()

if (type instanceof z.ZodNullable) {
const innerType = type.unwrap() as z.ZodType
return getFieldType(innerType)
}

if (type instanceof ZodOptional) {
const innerType = type.unwrap()

if (type instanceof z.ZodOptional) {
const innerType = type.unwrap() as z.ZodType
return getFieldType(innerType)
}

if (type instanceof ZodEffects) {
const innerType = type.innerType()

if (type instanceof z.ZodPipe) {
const innerType = type.def.in as z.ZodType
return getFieldType(innerType)
}

return "unsupported"
}

export function getIsFieldOptional(type: ZodType) {
export function getIsFieldOptional(type: z.ZodType) {
return (
type instanceof ZodOptional ||
type instanceof ZodNull ||
type instanceof ZodUndefined ||
type instanceof ZodNullable
type instanceof z.ZodOptional ||
type instanceof z.ZodNull ||
type instanceof z.ZodUndefined ||
type instanceof z.ZodNullable
)
}
59 changes: 39 additions & 20 deletions packages/admin/dashboard/src/dashboard-app/forms/hooks.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { FieldValues, useForm, UseFormProps } from "react-hook-form"
import { z, ZodEffects, ZodObject } from "zod"
import { z, ZodObject } from "zod"

import { ConfigField } from "../types"

interface UseExtendableFormProps<
TSchema extends ZodObject<any> | ZodEffects<ZodObject<any>>,
TSchema extends ZodObject<any>,
TContext = any,
TData = any
> extends Omit<UseFormProps<z.infer<TSchema>, TContext>, "resolver"> {
schema: TSchema
schema: TSchema | z.ZodPipe<TSchema, z.ZodType>
configs: ConfigField[]
data?: TData
}
Expand All @@ -21,26 +21,45 @@ function createAdditionalDataSchema(configs: ConfigField[]) {
}, {} as Record<string, z.ZodTypeAny>)
}

function createExtendedSchema<
TSchema extends ZodObject<any> | ZodEffects<ZodObject<any>>
>(baseSchema: TSchema, additionalDataSchema: Record<string, z.ZodTypeAny>) {
/**
* Extracts the shape from a schema, handling both ZodObject and ZodPipe.
* ZodPipe is created by .transform() and wraps the original schema.
*/
function getSchemaShape(
schema: ZodObject<any> | z.ZodPipe<ZodObject<any>, z.ZodType>
): z.ZodRawShape {
if (schema instanceof z.ZodPipe) {
const innerSchema = schema.def.in
// Guard: ensure inner schema is a ZodObject before accessing shape
if (innerSchema instanceof ZodObject) {
return innerSchema.shape
}
throw new Error(
"Expected ZodPipe to contain a ZodObject. Schema extensions require the base schema to be a ZodObject."
)
}
return schema.shape
}

function createExtendedSchema(
baseSchema: ZodObject<any> | z.ZodPipe<ZodObject<any>, z.ZodType>,
additionalDataSchema: Record<string, z.ZodTypeAny>
) {
const baseShape = getSchemaShape(baseSchema)

const extendedObjectSchema = z.object({
...(baseSchema instanceof ZodEffects
? baseSchema.innerType().shape
: baseSchema.shape),
...baseShape,
additional_data: z.object(additionalDataSchema).optional(),
})

return baseSchema instanceof ZodEffects
? baseSchema
.superRefine((data, ctx) => {
const result = extendedObjectSchema.safeParse(data)
if (!result.success) {
result.error.issues.forEach((issue) => ctx.addIssue(issue))
}
})
.and(extendedObjectSchema)
: extendedObjectSchema
return baseSchema
.superRefine((data, ctx) => {
const result = extendedObjectSchema.safeParse(data)
if (!result.success) {
result.error.issues.forEach((issue) => ctx.addIssue({ ...issue }))
}
})
.and(extendedObjectSchema)
}

function createExtendedDefaultValues<TData>(
Expand All @@ -60,7 +79,7 @@ function createExtendedDefaultValues<TData>(
}

export const useExtendableForm = <
TSchema extends ZodObject<any> | ZodEffects<ZodObject<any>>,
TSchema extends ZodObject<any>,
TContext = any,
TTransformedValues extends FieldValues | undefined = undefined
>({
Expand Down
6 changes: 3 additions & 3 deletions packages/admin/dashboard/src/dashboard-app/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from "@medusajs/admin-shared"
import { ComponentType } from "react"
import { LoaderFunction } from "react-router-dom"
import { ZodFirstPartySchemaTypes } from "zod"
import { z } from "zod"
import { INavItem } from "../components/layout/nav-item"

export type RouteExtension = {
Expand Down Expand Up @@ -39,7 +39,7 @@ export type DisplayExtension = {
}

export type FormFieldExtension = {
validation: ZodFirstPartySchemaTypes
validation: z.ZodTypeAny
Component?: ComponentType<any>
label?: string
description?: string
Expand All @@ -54,7 +54,7 @@ export type FormExtension = {

export type ConfigFieldExtension = {
defaultValue: ((data: any) => any) | any
validation: ZodFirstPartySchemaTypes
validation: z.ZodTypeAny
}

export type ConfigExtension = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ const LocationQuantitySchema = z.object({
disabledToggle: z.boolean(),
})

const InventoryLocationsSchema = z.record(LocationQuantitySchema)
const InventoryLocationsSchema = z.record(z.string(), LocationQuantitySchema)

const InventoryItemSchema = z.object({
locations: InventoryLocationsSchema,
})

export const InventoryStockSchema = z.object({
inventory_items: z.record(InventoryItemSchema),
inventory_items: z.record(z.string(), InventoryItemSchema),
})

export type InventoryLocationsSchema = z.infer<typeof InventoryLocationsSchema>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ export type PriceListCreateRegionPriceSchema = z.infer<
>

const PriceListCreateProductVariantSchema = z.object({
currency_prices: z.record(PriceListCreateCurrencyPriceSchema.optional()),
region_prices: z.record(PriceListCreateRegionPriceSchema.optional()),
currency_prices: z.record(z.string(), PriceListCreateCurrencyPriceSchema.optional()),
region_prices: z.record(z.string(), PriceListCreateRegionPriceSchema.optional()),
})

export type PriceListCreateProductVariantSchema = z.infer<
typeof PriceListCreateProductVariantSchema
>

const PriceListCreateProductVariantsSchema = z.record(
z.string(),
PriceListCreateProductVariantSchema
)

Expand All @@ -47,6 +48,7 @@ export type PriceListCreateProductVariantsSchema = z.infer<
>

export const PriceListCreateProductsSchema = z.record(
z.string(),
z.object({
variants: PriceListCreateProductVariantsSchema,
})
Expand Down Expand Up @@ -75,9 +77,10 @@ export type PriceListUpdateRegionPrice = z.infer<
>

export const PriceListUpdateProductVariantsSchema = z.record(
z.string(),
z.object({
currency_prices: z.record(PriceListUpdateCurrencyPriceSchema.optional()),
region_prices: z.record(PriceListUpdateRegionPriceSchema.optional()),
currency_prices: z.record(z.string(), PriceListUpdateCurrencyPriceSchema.optional()),
region_prices: z.record(z.string(), PriceListUpdateRegionPriceSchema.optional()),
})
)

Expand All @@ -86,6 +89,7 @@ export type PriceListUpdateProductVariantsSchema = z.infer<
>

export const PriceListUpdateProductsSchema = z.record(
z.string(),
z.object({
variants: PriceListUpdateProductVariantsSchema,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const ProductEditVariantSchema = z.object({
mid_code: z.string().optional(),
hs_code: z.string().optional(),
origin_country: z.string().optional(),
options: z.record(z.string()),
options: z.record(z.string(), z.string()),
})

// TODO: Either pass option ID or make the backend handle options constraints differently to handle the lack of IDs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const CreateProductVariantSchema = z.object({
manage_inventory: z.boolean().optional(),
allow_backorder: z.boolean().optional(),
inventory_kit: z.boolean().optional(),
options: z.record(z.string()),
options: z.record(z.string(), z.string()),
prices: zod
.record(zod.string(), zod.string().or(zod.number()).optional())
.optional(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ const LocationQuantitySchema = z.object({
disabledToggle: z.boolean(),
})

const ProductStockLocationsSchema = z.record(LocationQuantitySchema)
const ProductStockLocationsSchema = z.record(z.string(), LocationQuantitySchema)

const ProductStockInventoryItemSchema = z.object({
locations: ProductStockLocationsSchema,
})

const ProductStockVariantSchema = z.object({
inventory_items: z.record(ProductStockInventoryItemSchema),
inventory_items: z.record(z.string(), ProductStockInventoryItemSchema),
})

export const ProductStockSchema = z.object({
variants: z.record(ProductStockVariantSchema),
variants: z.record(z.string(), ProductStockVariantSchema),
})

export type ProductStockLocationSchema = z.infer<
Expand Down
2 changes: 1 addition & 1 deletion packages/core/framework/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
"morgan": "^1.9.1",
"path-to-regexp": "^8.2.0",
"tsconfig-paths": "^4.2.0",
"zod-validation-error": "3.5.1"
"zod-validation-error": "5.0.0"
},
"peerDependencies": {
"@medusajs/cli": "2.13.6",
Expand Down
6 changes: 3 additions & 3 deletions packages/core/framework/src/http/utils/validate-body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { zodValidator } from "../../zod"
export function validateAndTransformBody(
zodSchema:
| z.ZodObject<any, any>
| z.ZodEffects<any, any>
| z.ZodType<any, any, any>
| ((
customSchema?: z.ZodOptional<z.ZodNullable<z.ZodObject<any, any>>>
) => z.ZodObject<any, any> | z.ZodEffects<any, any>)
) => z.ZodObject<any, any> | z.ZodType<any, any, any>)
): (
req: MedusaRequest,
res: MedusaResponse,
Expand All @@ -21,7 +21,7 @@ export function validateAndTransformBody(
next: NextFunction
) {
try {
let schema: z.ZodObject<any, any> | z.ZodEffects<any, any>
let schema: z.ZodObject<any, any> | z.ZodType<any, any, any>
if (typeof zodSchema === "function") {
schema = zodSchema(req.additionalDataValidator)
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/framework/src/http/utils/validate-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const getFilterableFields = <T extends RequestQueryFields>(obj: T): T => {
}

export function validateAndTransformQuery<TEntity extends BaseEntity>(
zodSchema: z.ZodObject<any, any> | z.ZodEffects<any, any>,
zodSchema: z.ZodObject<any, any> | z.ZodType<any, any, any>,
queryConfig: QueryConfig<TEntity>
): (
req: MedusaRequest,
Expand Down
Loading
Loading