replace async-validator

This commit is contained in:
proddy
2026-06-19 12:06:33 +02:00
parent 392812d489
commit 92d63d6f5d
26 changed files with 204 additions and 42 deletions

View File

@@ -30,7 +30,6 @@
"@mui/material": "^9.1.1", "@mui/material": "^9.1.1",
"@table-library/react-table-library": "4.1.15", "@table-library/react-table-library": "4.1.15",
"alova": "^3.5.1", "alova": "^3.5.1",
"async-validator": "^4.2.5",
"etag": "^1.8.1", "etag": "^1.8.1",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"mime-types": "^3.0.2", "mime-types": "^3.0.2",

View File

@@ -29,9 +29,6 @@ importers:
alova: alova:
specifier: ^3.5.1 specifier: ^3.5.1
version: 3.5.1 version: 3.5.1
async-validator:
specifier: ^4.2.5
version: 4.2.5
etag: etag:
specifier: ^1.8.1 specifier: ^1.8.1
version: 1.8.1 version: 1.8.1
@@ -824,9 +821,6 @@ packages:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'} engines: {node: '>=8'}
async-validator@4.2.5:
resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
available-typed-arrays@1.0.7: available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -3749,8 +3743,6 @@ snapshots:
array-union@2.1.0: {} array-union@2.1.0: {}
async-validator@4.2.5: {}
available-typed-arrays@1.0.7: available-typed-arrays@1.0.7:
dependencies: dependencies:
possible-typed-array-names: 1.1.0 possible-typed-array-names: 1.1.0

View File

@@ -6,7 +6,6 @@ import type { Theme } from '@mui/material/styles';
import * as AuthenticationApi from 'components/routing/authentication'; import * as AuthenticationApi from 'components/routing/authentication';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
import type { ValidateFieldsError } from 'async-validator';
import { import {
LanguageSelector, LanguageSelector,
ValidatedPasswordField, ValidatedPasswordField,
@@ -19,6 +18,7 @@ import { useI18nContext } from 'i18n/i18n-react';
import type { SignInRequest } from 'types'; import type { SignInRequest } from 'types';
import { onEnterCallback, updateValue } from 'utils'; import { onEnterCallback, updateValue } from 'utils';
import { SIGN_IN_REQUEST_VALIDATOR, ValidationError, validate } from 'validators'; import { SIGN_IN_REQUEST_VALIDATOR, ValidationError, validate } from 'validators';
import type { ValidateFieldsError } from 'validators/schema';
const SignIn = memo(() => { const SignIn = memo(() => {
const authenticationContext = useContext(AuthenticationContext); const authenticationContext = useContext(AuthenticationContext);

View File

@@ -18,13 +18,13 @@ import {
import { callAction } from '@/api/app'; import { callAction } from '@/api/app';
import { dialogStyle } from 'CustomTheme'; import { dialogStyle } from 'CustomTheme';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
import type Schema from 'async-validator';
import type { ValidateFieldsError } from 'async-validator';
import { ValidatedTextField } from 'components'; import { ValidatedTextField } from 'components';
import { toast } from 'components/toast'; import { toast } from 'components/toast';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { updateValue } from 'utils'; import { updateValue } from 'utils';
import { ValidationError, validate } from 'validators'; import { ValidationError, validate } from 'validators';
import type Schema from 'validators/schema';
import type { ValidateFieldsError } from 'validators/schema';
import type { CommandItem } from './types'; import type { CommandItem } from './types';

View File

@@ -23,12 +23,12 @@ import {
} from '@mui/material'; } from '@mui/material';
import { dialogStyle } from 'CustomTheme'; import { dialogStyle } from 'CustomTheme';
import type Schema from 'async-validator';
import type { ValidateFieldsError } from 'async-validator';
import { BlockFormControlLabel, ValidatedTextField } from 'components'; import { BlockFormControlLabel, ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils'; import { numberValue, updateValue } from 'utils';
import { ValidationError, validate } from 'validators'; 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 { DeviceValueType, DeviceValueTypeNames, DeviceValueUOM_s } from './types';
import type { EntityItem } from './types'; import type { EntityItem } from './types';

View File

@@ -22,13 +22,13 @@ import {
import { callAction } from '@/api/app'; import { callAction } from '@/api/app';
import { dialogStyle } from 'CustomTheme'; import { dialogStyle } from 'CustomTheme';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
import type Schema from 'async-validator';
import type { ValidateFieldsError } from 'async-validator';
import { ValidatedTextField } from 'components'; import { ValidatedTextField } from 'components';
import { toast } from 'components/toast'; import { toast } from 'components/toast';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils'; import { numberValue, updateValue } from 'utils';
import { ValidationError, validate } from 'validators'; import { ValidationError, validate } from 'validators';
import type Schema from 'validators/schema';
import type { ValidateFieldsError } from 'validators/schema';
import { DeviceValueUOM, DeviceValueUOM_s } from './types'; import { DeviceValueUOM, DeviceValueUOM_s } from './types';
import type { DeviceValue } from './types'; import type { DeviceValue } from './types';

View File

@@ -22,12 +22,12 @@ import {
} from '@mui/material'; } from '@mui/material';
import { dialogStyle } from 'CustomTheme'; import { dialogStyle } from 'CustomTheme';
import type Schema from 'async-validator';
import type { ValidateFieldsError } from 'async-validator';
import { BlockFormControlLabel, ValidatedTextField } from 'components'; import { BlockFormControlLabel, ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { updateValue } from 'utils'; import { updateValue } from 'utils';
import { ValidationError, validate } from 'validators'; import { ValidationError, validate } from 'validators';
import type Schema from 'validators/schema';
import type { ValidateFieldsError } from 'validators/schema';
import { ScheduleFlag } from './types'; import { ScheduleFlag } from './types';
import type { ScheduleItem } from './types'; import type { ScheduleItem } from './types';

View File

@@ -18,12 +18,12 @@ import {
} from '@mui/material'; } from '@mui/material';
import { dialogStyle } from 'CustomTheme'; import { dialogStyle } from 'CustomTheme';
import type Schema from 'async-validator';
import type { ValidateFieldsError } from 'async-validator';
import { ValidatedTextField } from 'components'; import { ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils'; import { numberValue, updateValue } from 'utils';
import { ValidationError, validate } from 'validators'; 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 { AnalogType, AnalogTypeNames, DeviceValueUOM_s } from './types';
import type { AnalogSensor } from './types'; import type { AnalogSensor } from './types';

View File

@@ -16,12 +16,12 @@ import {
} from '@mui/material'; } from '@mui/material';
import { dialogStyle } from 'CustomTheme'; import { dialogStyle } from 'CustomTheme';
import type Schema from 'async-validator';
import type { ValidateFieldsError } from 'async-validator';
import { ValidatedTextField } from 'components'; import { ValidatedTextField } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils'; import { numberValue, updateValue } from 'utils';
import { ValidationError, validate } from 'validators'; import { ValidationError, validate } from 'validators';
import type Schema from 'validators/schema';
import type { ValidateFieldsError } from 'validators/schema';
import type { TemperatureSensor } from './types'; import type { TemperatureSensor } from './types';

View File

@@ -1,5 +1,5 @@
import Schema from 'async-validator'; import Schema from 'validators/schema';
import type { InternalRuleItem } from 'async-validator'; import type { InternalRuleItem } from 'validators/schema';
import { IP_OR_HOSTNAME_VALIDATOR } from 'validators/shared'; import { IP_OR_HOSTNAME_VALIDATOR } from 'validators/shared';
import type { import type {

View File

@@ -6,7 +6,6 @@ import { Button, Checkbox, MenuItem } from '@mui/material';
import * as APApi from 'api/ap'; import * as APApi from 'api/ap';
import type { ValidateFieldsError } from 'async-validator';
import { import {
BlockFormControlLabel, BlockFormControlLabel,
BlockNavigation, BlockNavigation,
@@ -22,6 +21,7 @@ import type { APSettingsType } from 'types';
import { APProvisionMode } from 'types'; import { APProvisionMode } from 'types';
import { numberValue, updateValueDirty, useRest } from 'utils'; import { numberValue, updateValueDirty, useRest } from 'utils';
import { ValidationError, createAPSettingsValidator, validate } from 'validators'; import { ValidationError, createAPSettingsValidator, validate } from 'validators';
import type { ValidateFieldsError } from 'validators/schema';
export const isAPEnabled = ({ provision_mode }: APSettingsType) => export const isAPEnabled = ({ provision_mode }: APSettingsType) =>
provision_mode === APProvisionMode.AP_MODE_DISCONNECTED; provision_mode === APProvisionMode.AP_MODE_DISCONNECTED;

View File

@@ -19,7 +19,6 @@ import { readSystemStatus } from 'api/system';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
import SystemMonitor from 'app/status/SystemMonitor'; import SystemMonitor from 'app/status/SystemMonitor';
import type { ValidateFieldsError } from 'async-validator';
import { import {
BlockFormControlLabel, BlockFormControlLabel,
BlockNavigation, BlockNavigation,
@@ -35,6 +34,7 @@ import { toast } from 'components/toast';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValueDirty, useRest } from 'utils'; import { numberValue, updateValueDirty, useRest } from 'utils';
import { ValidationError, validate } from 'validators'; import { ValidationError, validate } from 'validators';
import type { ValidateFieldsError } from 'validators/schema';
import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app'; import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app';
import { BOARD_PROFILES } from '../main/types'; import { BOARD_PROFILES } from '../main/types';

View File

@@ -16,7 +16,6 @@ import {
import * as MqttApi from 'api/mqtt'; import * as MqttApi from 'api/mqtt';
import type { ValidateFieldsError } from 'async-validator';
import { import {
BlockFormControlLabel, BlockFormControlLabel,
BlockNavigation, BlockNavigation,
@@ -32,6 +31,7 @@ import { useI18nContext } from 'i18n/i18n-react';
import type { MqttSettingsType } from 'types'; import type { MqttSettingsType } from 'types';
import { numberValue, updateValueDirty, useRest } from 'utils'; import { numberValue, updateValueDirty, useRest } from 'utils';
import { ValidationError, createMqttSettingsValidator, validate } from 'validators'; import { ValidationError, createMqttSettingsValidator, validate } from 'validators';
import type { ValidateFieldsError } from 'validators/schema';
import { callAction } from '../../api/app'; import { callAction } from '../../api/app';

View File

@@ -22,7 +22,6 @@ import { readNTPSettings } from 'api/ntp';
import { dialogStyle } from 'CustomTheme'; import { dialogStyle } from 'CustomTheme';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
import { updateState } from 'alova/client'; import { updateState } from 'alova/client';
import type { ValidateFieldsError } from 'async-validator';
import { import {
BlockFormControlLabel, BlockFormControlLabel,
BlockNavigation, BlockNavigation,
@@ -38,6 +37,7 @@ import type { NTPSettingsType, Time } from 'types';
import { formatLocalDateTime, updateValueDirty, useRest } from 'utils'; import { formatLocalDateTime, updateValueDirty, useRest } from 'utils';
import { ValidationError, validate } from 'validators'; import { ValidationError, validate } from 'validators';
import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp'; import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp';
import type { ValidateFieldsError } from 'validators/schema';
import { TIME_ZONES, selectedTimeZone, useTimeZoneSelectItems } from './TZ'; import { TIME_ZONES, selectedTimeZone, useTimeZoneSelectItems } from './TZ';

View File

@@ -25,7 +25,6 @@ import { API } from 'api/app';
import { updateState, useRequest } from 'alova/client'; import { updateState, useRequest } from 'alova/client';
import type { APIcall } from 'app/main/types'; import type { APIcall } from 'app/main/types';
import type { ValidateFieldsError } from 'async-validator';
import { import {
BlockFormControlLabel, BlockFormControlLabel,
BlockNavigation, BlockNavigation,
@@ -42,6 +41,7 @@ import type { NetworkSettingsType } from 'types';
import { updateValueDirty, useRest } from 'utils'; import { updateValueDirty, useRest } from 'utils';
import { ValidationError, validate } from 'validators'; import { ValidationError, validate } from 'validators';
import { createNetworkSettingsValidator } from 'validators/network'; import { createNetworkSettingsValidator } from 'validators/network';
import type { ValidateFieldsError } from 'validators/schema';
import SystemMonitor from '../../status/SystemMonitor'; import SystemMonitor from '../../status/SystemMonitor';
import { WiFiConnectionContext } from './WiFiConnectionContext'; import { WiFiConnectionContext } from './WiFiConnectionContext';

View File

@@ -6,7 +6,6 @@ import { Button } from '@mui/material';
import * as SecurityApi from 'api/security'; import * as SecurityApi from 'api/security';
import type { ValidateFieldsError } from 'async-validator';
import { import {
BlockNavigation, BlockNavigation,
ButtonRow, ButtonRow,
@@ -20,6 +19,7 @@ import { useI18nContext } from 'i18n/i18n-react';
import type { SecuritySettingsType } from 'types'; import type { SecuritySettingsType } from 'types';
import { updateValueDirty, useRest } from 'utils'; import { updateValueDirty, useRest } from 'utils';
import { SECURITY_SETTINGS_VALIDATOR, ValidationError, validate } from 'validators'; import { SECURITY_SETTINGS_VALIDATOR, ValidationError, validate } from 'validators';
import type { ValidateFieldsError } from 'validators/schema';
const SecuritySettings = () => { const SecuritySettings = () => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();

View File

@@ -14,8 +14,6 @@ import {
} from '@mui/material'; } from '@mui/material';
import { dialogStyle } from 'CustomTheme'; import { dialogStyle } from 'CustomTheme';
import type Schema from 'async-validator';
import type { ValidateFieldsError } from 'async-validator';
import { import {
BlockFormControlLabel, BlockFormControlLabel,
ValidatedPasswordField, ValidatedPasswordField,
@@ -25,6 +23,8 @@ import { useI18nContext } from 'i18n/i18n-react';
import type { UserType } from 'types'; import type { UserType } from 'types';
import { updateValue } from 'utils'; import { updateValue } from 'utils';
import { ValidationError, validate } from 'validators'; import { ValidationError, validate } from 'validators';
import type Schema from 'validators/schema';
import type { ValidateFieldsError } from 'validators/schema';
interface UserFormProps { interface UserFormProps {
creating: boolean; creating: boolean;

View File

@@ -4,7 +4,7 @@ import type { FC } from 'react';
import { FormHelperText, TextField } from '@mui/material'; import { FormHelperText, TextField } from '@mui/material';
import type { TextFieldProps } from '@mui/material'; import type { TextFieldProps } from '@mui/material';
import type { ValidateFieldsError } from 'async-validator'; import type { ValidateFieldsError } from 'validators/schema';
interface ValidatedFieldProps { interface ValidatedFieldProps {
fieldErrors?: ValidateFieldsError; fieldErrors?: ValidateFieldsError;

View File

@@ -1,6 +1,6 @@
import { isAPEnabled } from 'app/settings/APSettings'; import { isAPEnabled } from 'app/settings/APSettings';
import Schema from 'async-validator';
import type { APSettingsType } from 'types'; import type { APSettingsType } from 'types';
import Schema from 'validators/schema';
import { IP_ADDRESS_VALIDATOR } from './shared'; import { IP_ADDRESS_VALIDATOR } from './shared';

View File

@@ -1,4 +1,4 @@
import Schema from 'async-validator'; import Schema from 'validators/schema';
export const SIGN_IN_REQUEST_VALIDATOR = new Schema({ export const SIGN_IN_REQUEST_VALIDATOR = new Schema({
username: { username: {

View File

@@ -1,5 +1,5 @@
import Schema from 'async-validator';
import type { MqttSettingsType } from 'types'; import type { MqttSettingsType } from 'types';
import Schema from 'validators/schema';
import { IP_OR_HOSTNAME_VALIDATOR } from './shared'; import { IP_OR_HOSTNAME_VALIDATOR } from './shared';

View File

@@ -1,5 +1,5 @@
import Schema from 'async-validator';
import type { NetworkSettingsType } from 'types'; import type { NetworkSettingsType } from 'types';
import Schema from 'validators/schema';
import { HOSTNAME_VALIDATOR, IP_ADDRESS_VALIDATOR } from './shared'; import { HOSTNAME_VALIDATOR, IP_ADDRESS_VALIDATOR } from './shared';

View File

@@ -1,4 +1,4 @@
import Schema from 'async-validator'; import Schema from 'validators/schema';
import { IP_OR_HOSTNAME_VALIDATOR } from './shared'; import { IP_OR_HOSTNAME_VALIDATOR } from './shared';

View File

@@ -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<string, ValidateError[]>;
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<void>;
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<string, RuleItem | RuleItem[]>;
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<string | undefined> => {
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<string | undefined> => {
// 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<string, unknown>,
_options?: ValidateOption,
callback?: ValidateCallback
): Promise<void> {
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);
}
}

View File

@@ -1,6 +1,6 @@
import Schema from 'async-validator';
import type { InternalRuleItem } from 'async-validator';
import type { UserType } from 'types'; 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 USERNAME_PATTERN = /^[a-zA-Z0-9_\\.]{1,24}$/;
const JWT_SECRET_MAX_LENGTH = 64; const JWT_SECRET_MAX_LENGTH = 64;

View File

@@ -2,8 +2,8 @@ import type {
InternalRuleItem, InternalRuleItem,
ValidateFieldsError, ValidateFieldsError,
ValidateOption ValidateOption
} from 'async-validator'; } from 'validators/schema';
import type Schema from 'async-validator'; import type Schema from 'validators/schema';
export class ValidationError extends Error { export class ValidationError extends Error {
readonly fieldErrors: ValidateFieldsError; readonly fieldErrors: ValidateFieldsError;