From 92d63d6f5dbb5dab4f7c403cff582475b52cce67 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 19 Jun 2026 12:06:33 +0200 Subject: [PATCH] replace async-validator --- interface/package.json | 1 - interface/pnpm-lock.yaml | 8 - interface/src/SignIn.tsx | 2 +- interface/src/app/main/CommandsDialog.tsx | 4 +- .../src/app/main/CustomEntitiesDialog.tsx | 4 +- interface/src/app/main/DevicesDialog.tsx | 4 +- interface/src/app/main/SchedulerDialog.tsx | 4 +- .../src/app/main/SensorsAnalogDialog.tsx | 4 +- .../src/app/main/SensorsTemperatureDialog.tsx | 4 +- interface/src/app/main/validators.ts | 4 +- interface/src/app/settings/APSettings.tsx | 2 +- .../src/app/settings/ApplicationSettings.tsx | 2 +- interface/src/app/settings/MqttSettings.tsx | 2 +- interface/src/app/settings/NTPSettings.tsx | 2 +- .../app/settings/network/NetworkSettings.tsx | 2 +- .../settings/security/SecuritySettings.tsx | 2 +- interface/src/app/settings/security/User.tsx | 4 +- .../components/inputs/ValidatedTextField.tsx | 2 +- interface/src/validators/ap.ts | 2 +- interface/src/validators/authentication.ts | 2 +- interface/src/validators/mqtt.ts | 2 +- interface/src/validators/network.ts | 2 +- interface/src/validators/ntp.ts | 2 +- interface/src/validators/schema.ts | 171 ++++++++++++++++++ interface/src/validators/security.ts | 4 +- interface/src/validators/shared.ts | 4 +- 26 files changed, 204 insertions(+), 42 deletions(-) create mode 100644 interface/src/validators/schema.ts diff --git a/interface/package.json b/interface/package.json index 03edc07af..ad0d7a710 100644 --- a/interface/package.json +++ b/interface/package.json @@ -30,7 +30,6 @@ "@mui/material": "^9.1.1", "@table-library/react-table-library": "4.1.15", "alova": "^3.5.1", - "async-validator": "^4.2.5", "etag": "^1.8.1", "jwt-decode": "^4.0.0", "mime-types": "^3.0.2", diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index 8c2375f2d..f7bb2c04a 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -29,9 +29,6 @@ importers: alova: specifier: ^3.5.1 version: 3.5.1 - async-validator: - specifier: ^4.2.5 - version: 4.2.5 etag: specifier: ^1.8.1 version: 1.8.1 @@ -824,9 +821,6 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - async-validator@4.2.5: - resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} - available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -3749,8 +3743,6 @@ snapshots: array-union@2.1.0: {} - async-validator@4.2.5: {} - available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 diff --git a/interface/src/SignIn.tsx b/interface/src/SignIn.tsx index 8f7897769..00f8f36de 100644 --- a/interface/src/SignIn.tsx +++ b/interface/src/SignIn.tsx @@ -6,7 +6,6 @@ import type { Theme } from '@mui/material/styles'; import * as AuthenticationApi from 'components/routing/authentication'; import { useRequest } from 'alova/client'; -import type { ValidateFieldsError } from 'async-validator'; import { LanguageSelector, ValidatedPasswordField, @@ -19,6 +18,7 @@ import { useI18nContext } from 'i18n/i18n-react'; import type { SignInRequest } from 'types'; import { onEnterCallback, updateValue } from 'utils'; import { SIGN_IN_REQUEST_VALIDATOR, ValidationError, validate } from 'validators'; +import type { ValidateFieldsError } from 'validators/schema'; const SignIn = memo(() => { const authenticationContext = useContext(AuthenticationContext); diff --git a/interface/src/app/main/CommandsDialog.tsx b/interface/src/app/main/CommandsDialog.tsx index b02e183f2..5e7c7bc80 100644 --- a/interface/src/app/main/CommandsDialog.tsx +++ b/interface/src/app/main/CommandsDialog.tsx @@ -18,13 +18,13 @@ import { import { callAction } from '@/api/app'; import { dialogStyle } from 'CustomTheme'; import { useRequest } from 'alova/client'; -import type Schema from 'async-validator'; -import type { ValidateFieldsError } from 'async-validator'; import { ValidatedTextField } from 'components'; import { toast } from 'components/toast'; import { useI18nContext } from 'i18n/i18n-react'; import { updateValue } from 'utils'; import { ValidationError, validate } from 'validators'; +import type Schema from 'validators/schema'; +import type { ValidateFieldsError } from 'validators/schema'; import type { CommandItem } from './types'; diff --git a/interface/src/app/main/CustomEntitiesDialog.tsx b/interface/src/app/main/CustomEntitiesDialog.tsx index 8ede016b4..d63336738 100644 --- a/interface/src/app/main/CustomEntitiesDialog.tsx +++ b/interface/src/app/main/CustomEntitiesDialog.tsx @@ -23,12 +23,12 @@ import { } from '@mui/material'; import { dialogStyle } from 'CustomTheme'; -import type Schema from 'async-validator'; -import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, ValidatedTextField } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { numberValue, updateValue } from 'utils'; import { ValidationError, validate } from 'validators'; +import type Schema from 'validators/schema'; +import type { ValidateFieldsError } from 'validators/schema'; import { DeviceValueType, DeviceValueTypeNames, DeviceValueUOM_s } from './types'; import type { EntityItem } from './types'; diff --git a/interface/src/app/main/DevicesDialog.tsx b/interface/src/app/main/DevicesDialog.tsx index 1da86a81a..2ef5b7de8 100644 --- a/interface/src/app/main/DevicesDialog.tsx +++ b/interface/src/app/main/DevicesDialog.tsx @@ -22,13 +22,13 @@ import { import { callAction } from '@/api/app'; import { dialogStyle } from 'CustomTheme'; import { useRequest } from 'alova/client'; -import type Schema from 'async-validator'; -import type { ValidateFieldsError } from 'async-validator'; import { ValidatedTextField } from 'components'; import { toast } from 'components/toast'; import { useI18nContext } from 'i18n/i18n-react'; import { numberValue, updateValue } from 'utils'; import { ValidationError, validate } from 'validators'; +import type Schema from 'validators/schema'; +import type { ValidateFieldsError } from 'validators/schema'; import { DeviceValueUOM, DeviceValueUOM_s } from './types'; import type { DeviceValue } from './types'; diff --git a/interface/src/app/main/SchedulerDialog.tsx b/interface/src/app/main/SchedulerDialog.tsx index b1d120077..b75d12627 100644 --- a/interface/src/app/main/SchedulerDialog.tsx +++ b/interface/src/app/main/SchedulerDialog.tsx @@ -22,12 +22,12 @@ import { } from '@mui/material'; import { dialogStyle } from 'CustomTheme'; -import type Schema from 'async-validator'; -import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, ValidatedTextField } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { updateValue } from 'utils'; import { ValidationError, validate } from 'validators'; +import type Schema from 'validators/schema'; +import type { ValidateFieldsError } from 'validators/schema'; import { ScheduleFlag } from './types'; import type { ScheduleItem } from './types'; diff --git a/interface/src/app/main/SensorsAnalogDialog.tsx b/interface/src/app/main/SensorsAnalogDialog.tsx index 5073a2490..03bc18563 100644 --- a/interface/src/app/main/SensorsAnalogDialog.tsx +++ b/interface/src/app/main/SensorsAnalogDialog.tsx @@ -18,12 +18,12 @@ import { } from '@mui/material'; import { dialogStyle } from 'CustomTheme'; -import type Schema from 'async-validator'; -import type { ValidateFieldsError } from 'async-validator'; import { ValidatedTextField } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { numberValue, updateValue } from 'utils'; import { ValidationError, validate } from 'validators'; +import type Schema from 'validators/schema'; +import type { ValidateFieldsError } from 'validators/schema'; import { AnalogType, AnalogTypeNames, DeviceValueUOM_s } from './types'; import type { AnalogSensor } from './types'; diff --git a/interface/src/app/main/SensorsTemperatureDialog.tsx b/interface/src/app/main/SensorsTemperatureDialog.tsx index 714156a85..6c4194199 100644 --- a/interface/src/app/main/SensorsTemperatureDialog.tsx +++ b/interface/src/app/main/SensorsTemperatureDialog.tsx @@ -16,12 +16,12 @@ import { } from '@mui/material'; import { dialogStyle } from 'CustomTheme'; -import type Schema from 'async-validator'; -import type { ValidateFieldsError } from 'async-validator'; import { ValidatedTextField } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { numberValue, updateValue } from 'utils'; import { ValidationError, validate } from 'validators'; +import type Schema from 'validators/schema'; +import type { ValidateFieldsError } from 'validators/schema'; import type { TemperatureSensor } from './types'; diff --git a/interface/src/app/main/validators.ts b/interface/src/app/main/validators.ts index a1c2e7d26..58b9c98b1 100644 --- a/interface/src/app/main/validators.ts +++ b/interface/src/app/main/validators.ts @@ -1,5 +1,5 @@ -import Schema from 'async-validator'; -import type { InternalRuleItem } from 'async-validator'; +import Schema from 'validators/schema'; +import type { InternalRuleItem } from 'validators/schema'; import { IP_OR_HOSTNAME_VALIDATOR } from 'validators/shared'; import type { diff --git a/interface/src/app/settings/APSettings.tsx b/interface/src/app/settings/APSettings.tsx index 0f04aba3f..5f781c9c3 100644 --- a/interface/src/app/settings/APSettings.tsx +++ b/interface/src/app/settings/APSettings.tsx @@ -6,7 +6,6 @@ import { Button, Checkbox, MenuItem } from '@mui/material'; import * as APApi from 'api/ap'; -import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, BlockNavigation, @@ -22,6 +21,7 @@ import type { APSettingsType } from 'types'; import { APProvisionMode } from 'types'; import { numberValue, updateValueDirty, useRest } from 'utils'; import { ValidationError, createAPSettingsValidator, validate } from 'validators'; +import type { ValidateFieldsError } from 'validators/schema'; export const isAPEnabled = ({ provision_mode }: APSettingsType) => provision_mode === APProvisionMode.AP_MODE_DISCONNECTED; diff --git a/interface/src/app/settings/ApplicationSettings.tsx b/interface/src/app/settings/ApplicationSettings.tsx index d286770ba..876416e18 100644 --- a/interface/src/app/settings/ApplicationSettings.tsx +++ b/interface/src/app/settings/ApplicationSettings.tsx @@ -19,7 +19,6 @@ import { readSystemStatus } from 'api/system'; import { useRequest } from 'alova/client'; import SystemMonitor from 'app/status/SystemMonitor'; -import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, BlockNavigation, @@ -35,6 +34,7 @@ import { toast } from 'components/toast'; import { useI18nContext } from 'i18n/i18n-react'; import { numberValue, updateValueDirty, useRest } from 'utils'; import { ValidationError, validate } from 'validators'; +import type { ValidateFieldsError } from 'validators/schema'; import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app'; import { BOARD_PROFILES } from '../main/types'; diff --git a/interface/src/app/settings/MqttSettings.tsx b/interface/src/app/settings/MqttSettings.tsx index 4b26c6459..69dc1db15 100644 --- a/interface/src/app/settings/MqttSettings.tsx +++ b/interface/src/app/settings/MqttSettings.tsx @@ -16,7 +16,6 @@ import { import * as MqttApi from 'api/mqtt'; -import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, BlockNavigation, @@ -32,6 +31,7 @@ import { useI18nContext } from 'i18n/i18n-react'; import type { MqttSettingsType } from 'types'; import { numberValue, updateValueDirty, useRest } from 'utils'; import { ValidationError, createMqttSettingsValidator, validate } from 'validators'; +import type { ValidateFieldsError } from 'validators/schema'; import { callAction } from '../../api/app'; diff --git a/interface/src/app/settings/NTPSettings.tsx b/interface/src/app/settings/NTPSettings.tsx index f7d4cf6cc..18ce8ced8 100644 --- a/interface/src/app/settings/NTPSettings.tsx +++ b/interface/src/app/settings/NTPSettings.tsx @@ -22,7 +22,6 @@ import { readNTPSettings } from 'api/ntp'; import { dialogStyle } from 'CustomTheme'; import { useRequest } from 'alova/client'; import { updateState } from 'alova/client'; -import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, BlockNavigation, @@ -38,6 +37,7 @@ import type { NTPSettingsType, Time } from 'types'; import { formatLocalDateTime, updateValueDirty, useRest } from 'utils'; import { ValidationError, validate } from 'validators'; import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp'; +import type { ValidateFieldsError } from 'validators/schema'; import { TIME_ZONES, selectedTimeZone, useTimeZoneSelectItems } from './TZ'; diff --git a/interface/src/app/settings/network/NetworkSettings.tsx b/interface/src/app/settings/network/NetworkSettings.tsx index 6d4f44c07..ab6f4b503 100644 --- a/interface/src/app/settings/network/NetworkSettings.tsx +++ b/interface/src/app/settings/network/NetworkSettings.tsx @@ -25,7 +25,6 @@ import { API } from 'api/app'; import { updateState, useRequest } from 'alova/client'; import type { APIcall } from 'app/main/types'; -import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, BlockNavigation, @@ -42,6 +41,7 @@ import type { NetworkSettingsType } from 'types'; import { updateValueDirty, useRest } from 'utils'; import { ValidationError, validate } from 'validators'; import { createNetworkSettingsValidator } from 'validators/network'; +import type { ValidateFieldsError } from 'validators/schema'; import SystemMonitor from '../../status/SystemMonitor'; import { WiFiConnectionContext } from './WiFiConnectionContext'; diff --git a/interface/src/app/settings/security/SecuritySettings.tsx b/interface/src/app/settings/security/SecuritySettings.tsx index 40cac3de6..f4221bcc7 100644 --- a/interface/src/app/settings/security/SecuritySettings.tsx +++ b/interface/src/app/settings/security/SecuritySettings.tsx @@ -6,7 +6,6 @@ import { Button } from '@mui/material'; import * as SecurityApi from 'api/security'; -import type { ValidateFieldsError } from 'async-validator'; import { BlockNavigation, ButtonRow, @@ -20,6 +19,7 @@ import { useI18nContext } from 'i18n/i18n-react'; import type { SecuritySettingsType } from 'types'; import { updateValueDirty, useRest } from 'utils'; import { SECURITY_SETTINGS_VALIDATOR, ValidationError, validate } from 'validators'; +import type { ValidateFieldsError } from 'validators/schema'; const SecuritySettings = () => { const { LL } = useI18nContext(); diff --git a/interface/src/app/settings/security/User.tsx b/interface/src/app/settings/security/User.tsx index 093ad5d33..ddd79660e 100644 --- a/interface/src/app/settings/security/User.tsx +++ b/interface/src/app/settings/security/User.tsx @@ -14,8 +14,6 @@ import { } from '@mui/material'; import { dialogStyle } from 'CustomTheme'; -import type Schema from 'async-validator'; -import type { ValidateFieldsError } from 'async-validator'; import { BlockFormControlLabel, ValidatedPasswordField, @@ -25,6 +23,8 @@ import { useI18nContext } from 'i18n/i18n-react'; import type { UserType } from 'types'; import { updateValue } from 'utils'; import { ValidationError, validate } from 'validators'; +import type Schema from 'validators/schema'; +import type { ValidateFieldsError } from 'validators/schema'; interface UserFormProps { creating: boolean; diff --git a/interface/src/components/inputs/ValidatedTextField.tsx b/interface/src/components/inputs/ValidatedTextField.tsx index 1660af29d..1a705fabc 100644 --- a/interface/src/components/inputs/ValidatedTextField.tsx +++ b/interface/src/components/inputs/ValidatedTextField.tsx @@ -4,7 +4,7 @@ import type { FC } from 'react'; import { FormHelperText, TextField } from '@mui/material'; import type { TextFieldProps } from '@mui/material'; -import type { ValidateFieldsError } from 'async-validator'; +import type { ValidateFieldsError } from 'validators/schema'; interface ValidatedFieldProps { fieldErrors?: ValidateFieldsError; diff --git a/interface/src/validators/ap.ts b/interface/src/validators/ap.ts index 4aa5638a2..fe75857b7 100644 --- a/interface/src/validators/ap.ts +++ b/interface/src/validators/ap.ts @@ -1,6 +1,6 @@ import { isAPEnabled } from 'app/settings/APSettings'; -import Schema from 'async-validator'; import type { APSettingsType } from 'types'; +import Schema from 'validators/schema'; import { IP_ADDRESS_VALIDATOR } from './shared'; diff --git a/interface/src/validators/authentication.ts b/interface/src/validators/authentication.ts index e061f254a..676fbfe32 100644 --- a/interface/src/validators/authentication.ts +++ b/interface/src/validators/authentication.ts @@ -1,4 +1,4 @@ -import Schema from 'async-validator'; +import Schema from 'validators/schema'; export const SIGN_IN_REQUEST_VALIDATOR = new Schema({ username: { diff --git a/interface/src/validators/mqtt.ts b/interface/src/validators/mqtt.ts index c3ffc92f2..720ad5c6e 100644 --- a/interface/src/validators/mqtt.ts +++ b/interface/src/validators/mqtt.ts @@ -1,5 +1,5 @@ -import Schema from 'async-validator'; import type { MqttSettingsType } from 'types'; +import Schema from 'validators/schema'; import { IP_OR_HOSTNAME_VALIDATOR } from './shared'; diff --git a/interface/src/validators/network.ts b/interface/src/validators/network.ts index 5a4cb87dc..5295d01ea 100644 --- a/interface/src/validators/network.ts +++ b/interface/src/validators/network.ts @@ -1,5 +1,5 @@ -import Schema from 'async-validator'; import type { NetworkSettingsType } from 'types'; +import Schema from 'validators/schema'; import { HOSTNAME_VALIDATOR, IP_ADDRESS_VALIDATOR } from './shared'; diff --git a/interface/src/validators/ntp.ts b/interface/src/validators/ntp.ts index e59329b12..68bd3e19a 100644 --- a/interface/src/validators/ntp.ts +++ b/interface/src/validators/ntp.ts @@ -1,4 +1,4 @@ -import Schema from 'async-validator'; +import Schema from 'validators/schema'; import { IP_OR_HOSTNAME_VALIDATOR } from './shared'; diff --git a/interface/src/validators/schema.ts b/interface/src/validators/schema.ts new file mode 100644 index 000000000..fd489bb07 --- /dev/null +++ b/interface/src/validators/schema.ts @@ -0,0 +1,171 @@ +// Minimal drop-in replacement for the subset of `async-validator` used by this +// app's form validators. It intentionally mirrors async-validator's runtime +// semantics for the rule features in use (required, type string/number with +// min/max/pattern, and custom `validator(rule, value, callback)` functions) so +// the existing schema definitions and the `ValidateFieldsError` consumers keep +// working unchanged. +// +// Notable async-validator semantics preserved: +// - A non-required built-in rule is skipped when the value is "empty" +// (undefined / null / ''); `0` is NOT empty. +// - Custom `validator` functions are always invoked (they guard empties +// themselves), matching async-validator's behavior for validator rules. +// - `min`/`max` mean numeric bounds for `type: 'number'` and length bounds +// otherwise. +// - All rule errors for a field are collected (no early-exit per field). + +export interface ValidateError { + message?: string; + field?: string; + fieldValue?: unknown; +} + +export type ValidateFieldsError = Record; + +export interface InternalRuleItem { + field?: string; + fullField?: string; + [key: string]: unknown; +} + +export type RuleValidator = ( + rule: InternalRuleItem, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any, + callback: (error?: string | Error) => void +) => void | Promise; + +export interface RuleItem { + type?: string; + required?: boolean; + pattern?: RegExp | string; + min?: number; + max?: number; + message?: string; + validator?: RuleValidator; + [key: string]: unknown; +} + +export type Rules = Record; + +export interface ValidateOption { + first?: boolean; + firstFields?: boolean | string[]; + suppressWarning?: boolean; + [key: string]: unknown; +} + +export type ValidateCallback = ( + errors: ValidateError[] | null, + fields: ValidateFieldsError +) => void; + +const isEmpty = (value: unknown): boolean => + value === undefined || value === null || value === ''; + +const runValidator = async ( + rule: RuleItem, + field: string, + value: unknown +): Promise => { + let captured: string | undefined; + const callback = (error?: string | Error) => { + if (error) captured = typeof error === 'string' ? error : error.message; + }; + try { + const result = rule.validator!( + { ...rule, field, fullField: field }, + value, + callback + ); + if (result instanceof Promise) { + await result; + } + } catch (error) { + captured = error instanceof Error ? error.message : String(error); + } + return captured; +}; + +const runRule = async ( + rule: RuleItem, + field: string, + value: unknown +): Promise => { + // Custom validators own their empty-value handling and run unconditionally. + if (typeof rule.validator === 'function') { + return runValidator(rule, field, value); + } + + const empty = isEmpty(value); + + if (rule.required && empty) { + return rule.message ?? `${field} is required`; + } + + // Non-required built-in rules don't validate empty values. + if (empty) return undefined; + + if (rule.type === 'number') { + if (typeof value !== 'number' || Number.isNaN(value)) return rule.message; + if (typeof rule.min === 'number' && value < rule.min) return rule.message; + if (typeof rule.max === 'number' && value > rule.max) return rule.message; + return undefined; + } + + // type 'string' or any rule carrying length/pattern constraints. + if ( + rule.type === 'string' || + typeof rule.min === 'number' || + typeof rule.max === 'number' || + rule.pattern != null + ) { + const str = String(value); + if (typeof rule.min === 'number' && str.length < rule.min) return rule.message; + if (typeof rule.max === 'number' && str.length > rule.max) return rule.message; + if (rule.pattern != null) { + const re = + rule.pattern instanceof RegExp ? rule.pattern : new RegExp(rule.pattern); + if (!re.test(str)) return rule.message; + } + } + + return undefined; +}; + +export default class Schema { + private readonly rules: Rules; + + constructor(descriptor: Rules) { + this.rules = descriptor; + } + + // Mirrors async-validator's callback form. Always resolves (never rejects); + // callers (validators/shared.ts) inspect the `errors` argument. + async validate( + source: Record, + _options?: ValidateOption, + callback?: ValidateCallback + ): Promise { + const fields: ValidateFieldsError = {}; + const errors: ValidateError[] = []; + + for (const field of Object.keys(this.rules)) { + const ruleDef = this.rules[field]; + if (ruleDef === undefined) continue; + const ruleList = Array.isArray(ruleDef) ? ruleDef : [ruleDef]; + const value = source[field]; + + for (const rule of ruleList) { + const message = await runRule(rule, field, value); + if (message !== undefined) { + const error: ValidateError = { message, field, fieldValue: value }; + (fields[field] ??= []).push(error); + errors.push(error); + } + } + } + + callback?.(errors.length > 0 ? errors : null, fields); + } +} diff --git a/interface/src/validators/security.ts b/interface/src/validators/security.ts index 109aa045e..ff146779c 100644 --- a/interface/src/validators/security.ts +++ b/interface/src/validators/security.ts @@ -1,6 +1,6 @@ -import Schema from 'async-validator'; -import type { InternalRuleItem } from 'async-validator'; import type { UserType } from 'types'; +import Schema from 'validators/schema'; +import type { InternalRuleItem } from 'validators/schema'; const USERNAME_PATTERN = /^[a-zA-Z0-9_\\.]{1,24}$/; const JWT_SECRET_MAX_LENGTH = 64; diff --git a/interface/src/validators/shared.ts b/interface/src/validators/shared.ts index a0da4beb3..973651792 100644 --- a/interface/src/validators/shared.ts +++ b/interface/src/validators/shared.ts @@ -2,8 +2,8 @@ import type { InternalRuleItem, ValidateFieldsError, ValidateOption -} from 'async-validator'; -import type Schema from 'async-validator'; +} from 'validators/schema'; +import type Schema from 'validators/schema'; export class ValidationError extends Error { readonly fieldErrors: ValidateFieldsError;