Skip to content

login-identifier-schema

Edit

Accepts either a valid email address or canonical username for sign-in.

login-identifier-schema.ts
import * as z from "zod";
const EMAIL_MAX_LENGTH = 254;
const USERNAME_MIN_LENGTH = 3;
const USERNAME_MAX_LENGTH = 30;
const USERNAME_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
const codePointLength = (value: string): number => Array.from(value).length;
const loginEmailSchema = z
.email({ error: "Enter a valid email address" })
.check(
z.maxLength(EMAIL_MAX_LENGTH, {
error: `Email must contain at most ${EMAIL_MAX_LENGTH} characters`,
}),
)
.brand<"Email">();
const loginUsernameSchema = z
.string({ error: "Username must be a string" })
.check(
z.toLowerCase(),
z.normalize("NFC"),
z.refine(
(value) => codePointLength(value) >= USERNAME_MIN_LENGTH,
{
error: `Username must contain at least ${USERNAME_MIN_LENGTH} characters`,
},
),
z.refine(
(value) => codePointLength(value) <= USERNAME_MAX_LENGTH,
{
error: `Username must contain at most ${USERNAME_MAX_LENGTH} characters`,
},
),
z.regex(USERNAME_PATTERN, {
error:
"Username must use letters or numbers separated by single dots, underscores, or hyphens",
}),
)
.brand<"Username">();
/**
* Accepts either the email or canonical username policy after one trim pass.
*/
export const loginIdentifierSchema = z
.string({ error: "Login identifier must be a string" })
.check(z.trim())
.pipe(
z.union([loginEmailSchema, loginUsernameSchema], {
error: "Enter a valid email address or username",
}),
)
.meta({
title: "Login identifier",
description:
"A trimmed email address or canonical lowercase username for sign-in.",
examples: ["person@example.com", "person_42"],
});
export type LoginIdentifier = z.infer<typeof loginIdentifierSchema>;
Terminal
wget -O src/schemas/login-identifier-schema.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/zod-schemas/login-identifier-schema.ts
login-identifier-schema.example.ts
import {
loginIdentifierSchema,
type LoginIdentifier,
} from "./login-identifier-schema";
const identifier = loginIdentifierSchema.parse(" Person_42 ");
const startLogin = (value: LoginIdentifier) => {
console.log(`Signing in as ${value}`);
};
startLogin(identifier);
// Output: Signing in as person_42
  • input (string) — An email address or username. Outer whitespace is removed once before either format is evaluated.

LoginIdentifier, represented as Email | Username.

Email values preserve their casing and have a maximum length of 254. Username values are lowercased, normalized to NFC, limited to 3–30 Unicode code points, and restricted to the same alphanumeric and separator policy as usernameSchema. Invalid input reports one login-identifier error rather than requiring the caller to choose a branch.