gitdataai/src/lib/validation.ts
2026-04-15 09:08:09 +08:00

73 lines
1.8 KiB
TypeScript

/**
* Name validation for projects and repositories.
* Rules:
* 1. All English letters (stored as lowercase)
* 2. Cannot start with a number
* 3. Only letters, numbers, hyphens, and underscores
* 4. Cannot conflict with reserved route names
*/
export const RESERVED_ROUTE_NAMES = new Set([
'auth',
'init',
'user',
'settings',
'project',
'explore',
'market',
'docs',
'notify',
'api',
'admin',
'dashboard',
'login',
'register',
'logout',
'oauth',
'webhook',
'static',
]);
/** Regex: starts with letter, then letters/numbers/hyphens/underscores */
export const NAME_PATTERN = /^[a-z][a-z0-9_-]*$/;
/** Minimum length */
export const MIN_NAME_LENGTH = 2;
/** Maximum length */
export const MAX_NAME_LENGTH = 100;
export type NameValidationResult =
| { valid: true; message: '' }
| { valid: false; message: string };
/**
* Validates a project or repository name.
* Pass the raw input (before any transformation).
*/
export function validateName(raw: string): NameValidationResult {
const trimmed = raw.trim();
if (trimmed.length < MIN_NAME_LENGTH) {
return { valid: false, message: `Must be at least ${MIN_NAME_LENGTH} characters` };
}
if (trimmed.length > MAX_NAME_LENGTH) {
return { valid: false, message: `Must not exceed ${MAX_NAME_LENGTH} characters` };
}
const lower = trimmed.toLowerCase();
if (!NAME_PATTERN.test(lower)) {
return {
valid: false,
message: 'Must start with a letter and contain only letters, numbers, hyphens, and underscores',
};
}
if (RESERVED_ROUTE_NAMES.has(lower)) {
return { valid: false, message: `"${lower}" is a reserved name and cannot be used` };
}
return { valid: true, message: '' };
}