Files
frontend/src/features/auth/model/auth.schema.ts
T
ksv741 ca0ad4f824
Frontend verify / commits (pull_request) Successful in 28s
Frontend verify / security (pull_request) Successful in 10s
Frontend verify / verify (pull_request) Successful in 8m19s
refactor: enforce frontend view model boundaries
2026-09-28 18:48:23 +03:00

62 lines
1.5 KiB
TypeScript

import { z } from "zod";
import { validationMessage } from "@/shared/i18n";
const EMAIL_PART_COUNT = 2;
const ASCII_SPACE = 32;
const ASCII_DELETE = 127;
const MAX_EMAIL_LENGTH = 254;
const MIN_PASSWORD_BYTES = 12;
const MAX_PASSWORD_BYTES = 72;
function isEmailCharacterInvalid(character: string): boolean {
const codePoint = character.codePointAt(0);
return (
/\s/.test(character)
|| (codePoint !== undefined && codePoint < ASCII_SPACE)
|| codePoint === ASCII_DELETE
);
}
const email = z
.string()
.trim()
.toLowerCase()
.max(MAX_EMAIL_LENGTH)
.refine(
value =>
value.split("@").length === EMAIL_PART_COUNT
&& value.split("@").every(Boolean)
&& value.split("").every(element => !isEmailCharacterInvalid(element)),
{ error: validationMessage("auth.validation.invalidEmail") },
);
const password = z
.string()
.refine(
value =>
new TextEncoder().encode(value).length >= MIN_PASSWORD_BYTES
&& new TextEncoder().encode(value).length <= MAX_PASSWORD_BYTES,
{ error: validationMessage("auth.validation.invalidPasswordLength") },
);
const loginSchema = z.object({
email,
password,
passwordConfirmation: z.string().optional(),
});
const registerSchema = loginSchema.refine(
value => value.password === value.passwordConfirmation,
{
error: validationMessage("auth.validation.passwordsDoNotMatch"),
path: ["passwordConfirmation"],
},
);
type AuthFormValues = z.input<typeof loginSchema>;
export { type AuthFormValues, loginSchema, registerSchema };