chore(ui): upgrade zod to v4

This commit is contained in:
psychedelicious
2025-06-25 19:42:07 +10:00
parent 7948bca864
commit ab39305223
36 changed files with 67 additions and 91 deletions

View File

@@ -115,8 +115,8 @@
"use-debounce": "^10.0.4",
"use-device-pixel-ratio": "^1.1.2",
"uuid": "^11.1.0",
"zod": "^3.24.3",
"zod-validation-error": "^3.4.0"
"zod": "^3.25.67",
"zod-validation-error": "^3.5.2"
},
"peerDependencies": {
"react": "^18.2.0",
@@ -150,7 +150,6 @@
"openapi-types": "^12.1.3",
"openapi-typescript": "^7.6.1",
"prettier": "^3.5.3",
"rollup-plugin-visualizer": "^5.14.0",
"sonda": "^0.8.2",
"storybook": "^8.6.12",
"tsafe": "^1.8.5",

View File

@@ -195,11 +195,11 @@ dependencies:
specifier: ^11.1.0
version: 11.1.0
zod:
specifier: ^3.24.3
version: 3.24.3
specifier: ^3.25.67
version: 3.25.67
zod-validation-error:
specifier: ^3.4.0
version: 3.4.0(zod@3.24.3)
specifier: ^3.5.2
version: 3.5.2(zod@3.25.67)
devDependencies:
'@invoke-ai/eslint-config-react':
@@ -283,9 +283,6 @@ devDependencies:
prettier:
specifier: ^3.5.3
version: 3.5.3
rollup-plugin-visualizer:
specifier: ^5.14.0
version: 5.14.0
sonda:
specifier: ^0.8.2
version: 0.8.2
@@ -6193,8 +6190,8 @@ packages:
smol-toml: 1.3.4
strip-json-comments: 5.0.1
typescript: 5.8.3
zod: 3.24.3
zod-validation-error: 3.4.0(zod@3.24.3)
zod: 3.25.67
zod-validation-error: 3.5.2(zod@3.25.67)
dev: true
/kolorist@1.8.0:
@@ -7526,25 +7523,6 @@ packages:
semver-compare: 1.0.0
dev: false
/rollup-plugin-visualizer@5.14.0:
resolution: {integrity: sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==}
engines: {node: '>=18'}
hasBin: true
peerDependencies:
rolldown: 1.x
rollup: 2.x || 3.x || 4.x
peerDependenciesMeta:
rolldown:
optional: true
rollup:
optional: true
dependencies:
open: 8.4.2
picomatch: 4.0.2
source-map: 0.7.4
yargs: 17.7.2
dev: true
/rollup@2.79.2:
resolution: {integrity: sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==}
engines: {node: '>=10.0.0'}
@@ -7827,11 +7805,6 @@ packages:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
/source-map@0.7.4:
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
engines: {node: '>= 8'}
dev: true
/split-on-first@3.0.0:
resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==}
engines: {node: '>=12'}
@@ -8934,16 +8907,16 @@ packages:
engines: {node: '>=10'}
dev: true
/zod-validation-error@3.4.0(zod@3.24.3):
resolution: {integrity: sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==}
/zod-validation-error@3.5.2(zod@3.25.67):
resolution: {integrity: sha512-mdi7YOLtram5dzJ5aDtm1AG9+mxRma1iaMrZdYIpFO7epdKBUwLHIxTF8CPDeCQ828zAXYtizrKlEJAtzgfgrw==}
engines: {node: '>=18.0.0'}
peerDependencies:
zod: ^3.18.0
zod: ^3.25.0
dependencies:
zod: 3.24.3
zod: 3.25.67
/zod@3.24.3:
resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==}
/zod@3.25.67:
resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==}
/zustand@4.5.6(@types/react@18.3.11)(react@18.3.1):
resolution: {integrity: sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==}

View File

@@ -2,7 +2,7 @@ import { createLogWriter } from '@roarr/browser-log-writer';
import { atom } from 'nanostores';
import type { Logger, MessageSerializer } from 'roarr';
import { ROARR, Roarr } from 'roarr';
import { z } from 'zod';
import { z } from 'zod/v4';
const serializeMessage: MessageSerializer = (message) => {
return JSON.stringify(message);

View File

@@ -1,4 +1,4 @@
import type { z } from 'zod';
import type { z } from 'zod/v4';
/**
* Helper to create a type guard from a zod schema. The type guard will infer the schema's TS type.

View File

@@ -14,7 +14,7 @@ import { queueApi } from 'services/api/endpoints/queue';
import type { ImageDTO, S } from 'services/api/types';
import { $socket } from 'services/events/stores';
import { assert, objectEntries } from 'tsafe';
import { z } from 'zod';
import { z } from 'zod/v4';
const zAutoSwitchMode = z.enum(['off', 'switch_on_start', 'switch_on_finish']);
export const isAutoSwitchMode = buildZodTypeGuard(zAutoSwitchMode);

View File

@@ -6,7 +6,7 @@ import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
import { z } from 'zod';
import { z } from 'zod/v4';
const zMode = z.enum(['fill', 'contain', 'cover']);
type Mode = z.infer<typeof zMode>;

View File

@@ -4,7 +4,7 @@ import { zModelIdentifierField } from 'features/nodes/types/common';
import { Graph } from 'features/nodes/util/graph/generation/Graph';
import type { ControlLoRAModelConfig, ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/types';
import { assert } from 'tsafe';
import { z } from 'zod';
import { z } from 'zod/v4';
const zAjustImageChannels = z.enum([
'Red (RGBA)',

View File

@@ -21,7 +21,7 @@ import type { Invocation } from 'services/api/types';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
import { describe, test } from 'vitest';
import type { z } from 'zod';
import type { z } from 'zod/v4';
import type {
CanvasEntityIdentifier,

View File

@@ -32,7 +32,7 @@ import {
import { getImageDTOSafe } from 'services/api/endpoints/images';
import type { ImageDTO } from 'services/api/types';
import type { JsonObject } from 'type-fest';
import { z } from 'zod';
import { z } from 'zod/v4';
const zId = z.string().min(1);
const zName = z.string().min(1).nullable();

View File

@@ -20,7 +20,7 @@ import { useTranslation } from 'react-i18next';
import { uploadImages } from 'services/api/endpoints/images';
import { useBoardName } from 'services/api/hooks/useBoardName';
import type { UploadImageArg } from 'services/api/types';
import { z } from 'zod';
import { z } from 'zod/v4';
const ACCEPTED_IMAGE_TYPES = ['image/png', 'image/jpg', 'image/jpeg', 'image/webp'];
const ACCEPTED_FILE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp'];
@@ -43,13 +43,13 @@ const zUploadFile = z
(file) => {
return ACCEPTED_IMAGE_TYPES.includes(file.type);
},
(file) => ({ message: `File type ${file.type} is not supported` })
{ message: `File type is not supported` }
)
.refine(
(file) => {
return ACCEPTED_FILE_EXTENSIONS.some((ext) => file.name.endsWith(ext));
},
(file) => ({ message: `File extension .${file.name.split('.').at(-1)} is not supported` })
{ message: `File extension is not supported` }
);
const sx = {

View File

@@ -2,7 +2,7 @@ import type { PayloadAction, Selector } from '@reduxjs/toolkit';
import { createSelector, createSlice } from '@reduxjs/toolkit';
import type { PersistConfig, RootState } from 'app/store/store';
import { buildZodTypeGuard } from 'common/util/zodUtils';
import { z } from 'zod';
import { z } from 'zod/v4';
const zSeedBehaviour = z.enum(['PER_ITERATION', 'PER_PROMPT']);
export const isSeedBehaviour = buildZodTypeGuard(zSeedBehaviour);

View File

@@ -5,7 +5,7 @@ import { selectBoardsListOrderBy, selectBoardsListOrderDir } from 'features/gall
import { boardsListOrderByChanged, boardsListOrderDirChanged } from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { z } from 'zod/v4';
const zOrderBy = z.enum(['created_at', 'board_name']);
type OrderBy = z.infer<typeof zOrderBy>;

View File

@@ -11,7 +11,7 @@ import {
import type { ChangeEvent } from 'react';
import { useCallback, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { z } from 'zod/v4';
const zOrderBy = z.enum(['opened_at', 'created_at', 'updated_at', 'name']);
type OrderBy = z.infer<typeof zOrderBy>;

View File

@@ -127,7 +127,7 @@ import {
import { atom, computed } from 'nanostores';
import type { MouseEvent } from 'react';
import type { UndoableOptions } from 'redux-undo';
import type { z } from 'zod';
import type { z } from 'zod/v4';
import type { PendingConnection, Templates } from './types';
@@ -193,7 +193,7 @@ const getField = (nodeId: string, fieldName: string, state: NodesState) => {
const fieldValueReducer = <T extends FieldValue>(
state: NodesState,
action: FieldValueAction<T>,
schema: z.ZodTypeAny
schema: z.ZodType<T>
) => {
const { nodeId, fieldName, value } = action.payload;
const field = getField(nodeId, fieldName, state);

View File

@@ -142,6 +142,7 @@ export const collect: InvocationTemplate = {
name: 'item',
title: 'Collection Item',
required: false,
default: undefined,
description: 'The item to collect (all inputs must be of the same type)',
fieldKind: 'input',
input: 'connection',
@@ -330,6 +331,7 @@ export const img_resize: InvocationTemplate = {
name: 'metadata',
title: 'Metadata',
required: false,
default: undefined,
description: 'Optional metadata to be saved with the image',
fieldKind: 'input',
input: 'connection',
@@ -458,6 +460,7 @@ const iterate: InvocationTemplate = {
name: 'collection',
title: 'Collection',
required: false,
default: undefined,
description: 'The list of items to iterate over',
fieldKind: 'input',
input: 'connection',

View File

@@ -1,4 +1,4 @@
import { z } from 'zod';
import { z } from 'zod/v4';
import type { ModelIdentifier as ModelIdentifierV2 } from './v2/common';
import { zModelIdentifier as zModelIdentifierV2 } from './v2/common';

View File

@@ -6,7 +6,7 @@ import MersenneTwister from 'mtwist';
import { boardsApi } from 'services/api/endpoints/boards';
import { utilitiesApi } from 'services/api/endpoints/utilities';
import { assert } from 'tsafe';
import { z } from 'zod';
import { z } from 'zod/v4';
import type { ImageField } from './common';
import { zBoardField, zColorField, zImageField, zModelIdentifierField, zSchedulerField } from './common';
@@ -57,8 +57,9 @@ const zFieldInputTemplateBase = zFieldTemplateBase.extend({
fieldKind: z.literal('input'),
input: zFieldInput,
required: z.boolean(),
default: z.undefined(),
ui_component: zFieldUIComponent.nullish(),
ui_choice_labels: z.record(z.string()).nullish(),
ui_choice_labels: z.record(z.string(), z.string()).nullish(),
});
const zFieldOutputTemplateBase = zFieldTemplateBase.extend({
fieldKind: z.literal('output'),
@@ -655,7 +656,7 @@ const zEnumFieldInputTemplate = zFieldInputTemplateBase.extend({
originalType: zFieldType.optional(),
default: zEnumFieldValue,
options: z.array(z.string()),
labels: z.record(z.string()).optional(),
labels: z.record(z.string(), z.string()).optional(),
});
const zEnumFieldOutputTemplate = zFieldOutputTemplateBase.extend({
type: zEnumFieldType,

View File

@@ -1,5 +1,5 @@
import type { Edge, Node } from '@xyflow/react';
import { z } from 'zod';
import { z } from 'zod/v4';
import { zClassification, zProgressImage } from './common';
import { zFieldInputInstance, zFieldInputTemplate, zFieldOutputTemplate } from './field';
@@ -11,8 +11,8 @@ const zInvocationTemplate = z.object({
title: z.string(),
description: z.string(),
tags: z.array(z.string().min(1)),
inputs: z.record(zFieldInputTemplate),
outputs: z.record(zFieldOutputTemplate),
inputs: z.record(z.string(), zFieldInputTemplate),
outputs: z.record(z.string(), zFieldOutputTemplate),
outputType: z.string().min(1),
version: zSemVer,
useCache: z.boolean(),
@@ -30,7 +30,7 @@ export const zInvocationNodeData = z.object({
label: z.string(),
notes: z.string(),
type: z.string().trim().min(1),
inputs: z.record(zFieldInputInstance),
inputs: z.record(z.string(), zFieldInputInstance),
isOpen: z.boolean(),
isIntermediate: z.boolean(),
useCache: z.boolean(),

View File

@@ -1,4 +1,4 @@
import { z } from 'zod';
import { z } from 'zod/v4';
// Schemas and types for working with semver

View File

@@ -1,4 +1,4 @@
import { z } from 'zod';
import { z } from 'zod/v4';
// WorkflowV1 Schema
@@ -487,7 +487,7 @@ const zMetadataItemPolymorphicInputFieldValue = zInputFieldValueBase.extend({
value: z.union([zMetadataItemField, z.array(zMetadataItemField)]).optional(),
});
const zMetadataField = z.record(z.any());
const zMetadataField = z.record(z.string(), z.any());
const zMetadataInputFieldValue = zInputFieldValueBase.extend({
type: z.literal('MetadataField'),
@@ -607,8 +607,8 @@ const zInvocationNodeData = z.object({
// no easy way to build this dynamically, and we don't want to anyways, because this will be used
// to validate incoming workflows, and we want to allow community nodes.
type: z.string().trim().min(1),
inputs: z.record(zInputFieldValue),
outputs: z.record(zOutputFieldValue),
inputs: z.record(z.string(), zInputFieldValue),
outputs: z.record(z.string(), zOutputFieldValue),
label: z.string(),
isOpen: z.boolean(),
notes: z.string(),

View File

@@ -1,4 +1,4 @@
import { z } from 'zod';
import { z } from 'zod/v4';
// #region Field data schemas
export const zImageField = z.object({

View File

@@ -1,4 +1,4 @@
import { z } from 'zod';
import { z } from 'zod/v4';
import {
zBoardField,

View File

@@ -1,4 +1,4 @@
import { z } from 'zod';
import { z } from 'zod/v4';
import { zFieldInputInstance, zFieldOutputInstance } from './field';
import { zSemVer } from './semver';
@@ -14,8 +14,8 @@ export const zInvocationNodeData = z.object({
useCache: z.boolean(),
version: zSemVer,
nodePack: z.string().min(1).nullish(),
inputs: z.record(zFieldInputInstance),
outputs: z.record(zFieldOutputInstance),
inputs: z.record(z.string(), zFieldInputInstance),
outputs: z.record(z.string(), zFieldOutputInstance),
});
export const zNotesNodeData = z.object({

View File

@@ -1,4 +1,4 @@
import { z } from 'zod';
import { z } from 'zod/v4';
// Schemas and types for working with semver

View File

@@ -1,4 +1,4 @@
import { z } from 'zod';
import { z } from 'zod/v4';
import { zFieldIdentifier } from './field';
import { zInvocationNodeData, zNotesNodeData } from './invocation';

View File

@@ -1,5 +1,5 @@
import { getPrefixedId } from 'features/controlLayers/konva/util';
import { z } from 'zod';
import { z } from 'zod/v4';
import type { FieldType } from './field';
import { zFieldIdentifier } from './field';
@@ -64,7 +64,7 @@ export type ElementId = z.infer<typeof zElementId>;
const zElementBase = z.object({
id: zElementId,
parentId: zElementId.optional(),
data: z.undefined(),
data: z.undefined().optional(),
});
export const zNumberComponent = z.enum(['number-input', 'slider', 'number-input-and-slider']);
@@ -271,7 +271,7 @@ export const getDefaultForm = (): BuilderForm => {
};
const zBuilderForm = z.object({
elements: z.record(zFormElement),
elements: z.record(z.string(), zFormElement),
rootElementId: zElementId,
});

View File

@@ -3,7 +3,7 @@ import { Graph } from 'features/nodes/util/graph/generation/Graph';
import type { AnyInvocation, Invocation } from 'services/api/types';
import { assert, AssertionError, is } from 'tsafe';
import { describe, expect, it } from 'vitest';
import { z } from 'zod';
import { z } from 'zod/v4';
describe('Graph', () => {
describe('constructor', () => {

View File

@@ -10,7 +10,7 @@ import type { WorkflowV3 } from 'features/nodes/types/workflow';
import { zWorkflowV3 } from 'features/nodes/types/workflow';
import i18n from 'i18n';
import { useCallback } from 'react';
import { fromZodError } from 'zod-validation-error';
import { fromZodError } from 'zod-validation-error/v4';
const log = logger('workflows');

View File

@@ -13,7 +13,7 @@ import { zWorkflowV2 } from 'features/nodes/types/v2/workflow';
import type { WorkflowV3 } from 'features/nodes/types/workflow';
import { zWorkflowV3 } from 'features/nodes/types/workflow';
import { t } from 'i18next';
import { z } from 'zod';
import { z } from 'zod/v4';
/**
* Helper schema to extract the version from a workflow.

View File

@@ -2,7 +2,7 @@ import { NUMPY_RAND_MAX } from 'app/constants';
import { roundToMultiple } from 'common/util/roundDownToMultiple';
import { buildZodTypeGuard } from 'common/util/zodUtils';
import { zModelIdentifierField, zSchedulerField } from 'features/nodes/types/common';
import { z } from 'zod';
import { z } from 'zod/v4';
/**
* Schemas, types and type guards for parameters.

View File

@@ -1,5 +1,5 @@
import type { LogLevel, LogNamespace } from 'app/logging/logger';
import { z } from 'zod';
import { z } from 'zod/v4';
const zLanguage = z.enum([
'ar',

View File

@@ -1,4 +1,4 @@
import { z } from 'zod';
import { z } from 'zod/v4';
export const zPydanticValidationError = z.object({
status: z.literal(422),

View File

@@ -1,5 +1,5 @@
import { deepClone } from 'common/util/deepClone';
import { z } from 'zod';
import { z } from 'zod/v4';
const zTabName = z.enum(['generate', 'canvas', 'upscaling', 'workflows', 'models', 'queue']);
export const ALL_TABS = zTabName.options;

View File

@@ -11,8 +11,8 @@ import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { serializeError } from 'serialize-error';
import { checkBoardAccess, checkImageAccess, checkModelAccess } from 'services/api/hooks/accessChecks';
import { z } from 'zod';
import { fromZodError } from 'zod-validation-error';
import { z } from 'zod/v4';
import { fromZodError } from 'zod-validation-error/v4';
const log = logger('workflows');

View File

@@ -3,7 +3,7 @@ import { isRejectedWithValue } from '@reduxjs/toolkit';
import { $toastMap } from 'app/store/nanostores/toastMap';
import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { z } from 'zod';
import { z } from 'zod/v4';
const trialUsageErrorSubstring = 'usage allotment for the free trial';
const trialUsageErrorCode = 'USAGE_LIMIT_TRIAL';