Migrate from Zod v3 to v4 (#14639)

Closes [#1526](https://github.com/twentyhq/core-team-issues/issues/1526)

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
Abdul Rahman
2025-09-24 21:59:05 +05:30
committed by GitHub
parent 8d78032357
commit 3cada58908
98 changed files with 809 additions and 1075 deletions
@@ -1,22 +1,21 @@
import { z } from 'zod';
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { metadataLabelSchema } from '@/object-metadata/validation-schemas/metadataLabelSchema';
import { themeColorSchema } from 'twenty-ui/theme';
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
import { FieldMetadataType, RelationType } from '~/generated/graphql';
import { camelCaseStringSchema } from '~/utils/validation-schemas/camelCaseStringSchema';
export const fieldMetadataItemSchema = (existingLabels?: string[]) => {
return z.object({
__typename: z.literal('Field').optional(),
createdAt: z.string().datetime(),
createdAt: z.iso.datetime(),
defaultValue: z.any().optional(),
description: z.string().trim().nullable().optional(),
icon: z
.union([z.string().startsWith('Icon').trim(), z.literal('')])
.nullable()
.optional(),
id: z.string().uuid(),
id: z.uuid(),
isActive: z.boolean(),
isCustom: z.boolean(),
isNullable: z.boolean(),
@@ -30,7 +29,7 @@ export const fieldMetadataItemSchema = (existingLabels?: string[]) => {
.array(
z.object({
color: themeColorSchema,
id: z.string().uuid(),
id: z.uuid(),
label: z.string().trim().min(1),
position: z.number(),
value: z.string().trim().min(1),
@@ -42,33 +41,33 @@ export const fieldMetadataItemSchema = (existingLabels?: string[]) => {
relation: z
.object({
__typename: z.literal('Relation').optional(),
type: z.nativeEnum(RelationType),
type: z.enum(RelationType),
sourceFieldMetadata: z.object({
__typename: z.literal('Field').optional(),
id: z.string().uuid(),
id: z.uuid(),
name: z.string().trim().min(1),
}),
sourceObjectMetadata: z.object({
__typename: z.literal('Object').optional(),
id: z.string().uuid(),
id: z.uuid(),
namePlural: z.string().trim().min(1),
nameSingular: z.string().trim().min(1),
}),
targetFieldMetadata: z.object({
__typename: z.literal('Field').optional(),
id: z.string().uuid(),
id: z.uuid(),
name: z.string().trim().min(1),
}),
targetObjectMetadata: z.object({
__typename: z.literal('Object').optional(),
id: z.string().uuid(),
id: z.uuid(),
namePlural: z.string().trim().min(1),
nameSingular: z.string().trim().min(1),
}),
})
.nullable()
.optional(),
type: z.nativeEnum(FieldMetadataType),
updatedAt: z.string().datetime(),
}) satisfies z.ZodType<FieldMetadataItem>;
type: z.enum(FieldMetadataType),
updatedAt: z.iso.datetime(),
});
};
@@ -4,7 +4,7 @@ import { type IndexFieldMetadataItem } from '@/object-metadata/types/IndexFieldM
export const indexFieldMetadataItemSchema = z.object({
__typename: z.literal('IndexField'),
fieldMetadataId: z.string().uuid(),
fieldMetadataId: z.uuid(),
id: z.string(),
createdAt: z.string(),
updatedAt: z.string(),
@@ -2,16 +2,16 @@ import { z } from 'zod';
import { type IndexMetadataItem } from '@/object-metadata/types/IndexMetadataItem';
import { indexFieldMetadataItemSchema } from '@/object-metadata/validation-schemas/indexFieldMetadataItemSchema';
import { IndexType } from '~/generated-metadata/graphql';
import { IndexType } from '~/generated/graphql';
export const indexMetadataItemSchema = z.object({
__typename: z.literal('Index'),
id: z.string().uuid(),
id: z.uuid(),
name: z.string(),
indexFieldMetadatas: z.array(indexFieldMetadataItemSchema),
createdAt: z.string(),
updatedAt: z.string(),
indexType: z.nativeEnum(IndexType),
indexType: z.enum(IndexType),
indexWhereClause: z.string().nullable(),
isUnique: z.boolean(),
objectMetadata: z.any(),
@@ -1,6 +1,5 @@
import { z } from 'zod';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fieldMetadataItemSchema';
import { indexMetadataItemSchema } from '@/object-metadata/validation-schemas/indexMetadataItemSchema';
import { metadataLabelSchema } from '@/object-metadata/validation-schemas/metadataLabelSchema';
@@ -8,28 +7,28 @@ import { camelCaseStringSchema } from '~/utils/validation-schemas/camelCaseStrin
export const objectMetadataItemSchema = z.object({
__typename: z.literal('Object').optional(),
createdAt: z.string().datetime(),
createdAt: z.iso.datetime(),
description: z.string().trim().nullable().optional(),
fields: z.array(fieldMetadataItemSchema()),
readableFields: z.array(fieldMetadataItemSchema()),
updatableFields: z.array(fieldMetadataItemSchema()),
indexMetadatas: z.array(indexMetadataItemSchema),
icon: z.string().startsWith('Icon').trim(),
id: z.string().uuid(),
id: z.uuid(),
duplicateCriteria: z.array(z.array(z.string())),
imageIdentifierFieldMetadataId: z.string().uuid().nullable(),
imageIdentifierFieldMetadataId: z.uuid().nullable(),
isActive: z.boolean(),
isCustom: z.boolean(),
isRemote: z.boolean(),
isSystem: z.boolean(),
isUIReadOnly: z.boolean(),
isSearchable: z.boolean(),
labelIdentifierFieldMetadataId: z.string().uuid(),
labelIdentifierFieldMetadataId: z.uuid(),
labelPlural: metadataLabelSchema(),
labelSingular: metadataLabelSchema(),
namePlural: camelCaseStringSchema,
nameSingular: camelCaseStringSchema,
updatedAt: z.string().datetime(),
updatedAt: z.iso.datetime(),
shortcut: z.string().nullable().optional(),
isLabelSyncedWithName: z.boolean(),
}) satisfies z.ZodType<ObjectMetadataItem>;
});
@@ -1,6 +1,5 @@
import { z } from 'zod';
import { type FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem';
import { themeColorSchema } from 'twenty-ui/theme';
import { computeOptionValueFromLabel } from '~/pages/settings/data-model/utils/computeOptionValueFromLabel';
@@ -22,9 +21,9 @@ const selectOptionSchema = z
}
},
{
message: 'Label is not transliterable',
error: 'Label is not transliterable',
},
) satisfies z.ZodType<FieldMetadataItemOption>;
);
export const selectOptionsSchema = z
.array(selectOptionSchema)
@@ -35,7 +34,7 @@ export const selectOptionsSchema = z
return new Set(optionIds).size === options.length;
},
{
message: 'Options must have unique ids',
error: 'Options must have unique ids',
},
)
.refine(
@@ -44,13 +43,13 @@ export const selectOptionsSchema = z
return new Set(optionValues).size === options.length;
},
{
message: 'Options must have unique values',
error: 'Options must have unique values',
},
)
.refine(
(options) =>
[...options].sort().every((option, index) => option.position === index),
{
message: 'Options positions must be sequential',
error: 'Options positions must be sequential',
},
);