/**
* Input validation utilities
*/
import { ThoughtInput } from '../types.js';
export const VALIDATION_LIMITS = {
MAX_THOUGHT_LENGTH: 10000,
MAX_BRANCH_ID_LENGTH: 100,
MIN_THOUGHT_NUMBER: 1,
MIN_TOTAL_THOUGHTS: 1,
MAX_TOTAL_THOUGHTS: 1000,
} as const;
export class ValidationError extends Error {
constructor(message: string) {
super(message);
this.name = 'ValidationError';
}
}
/**
* Validate thought input with comprehensive checks
* @throws {ValidationError} If validation fails
*/
export function validateThoughtInput(input: ThoughtInput): void {
// Validate thought
if (!input.thought || typeof input.thought !== 'string') {
throw new ValidationError('thought is required and must be a string');
}
const trimmedThought = input.thought.trim();
if (trimmedThought.length === 0) {
throw new ValidationError('thought cannot be empty or only whitespace');
}
if (input.thought.length > VALIDATION_LIMITS.MAX_THOUGHT_LENGTH) {
throw new ValidationError(
`thought cannot exceed ${VALIDATION_LIMITS.MAX_THOUGHT_LENGTH} characters`
);
}
// Validate nextThoughtNeeded
if (typeof input.nextThoughtNeeded !== 'boolean') {
throw new ValidationError('nextThoughtNeeded is required and must be a boolean');
}
// Validate thoughtNumber
if (typeof input.thoughtNumber !== 'number') {
throw new ValidationError('thoughtNumber is required and must be a number');
}
if (!Number.isFinite(input.thoughtNumber)) {
throw new ValidationError('thoughtNumber must be a finite number');
}
if (!Number.isInteger(input.thoughtNumber)) {
throw new ValidationError('thoughtNumber must be an integer');
}
if (input.thoughtNumber < VALIDATION_LIMITS.MIN_THOUGHT_NUMBER) {
throw new ValidationError(
`thoughtNumber must be at least ${VALIDATION_LIMITS.MIN_THOUGHT_NUMBER}`
);
}
// Validate totalThoughts
if (typeof input.totalThoughts !== 'number') {
throw new ValidationError('totalThoughts is required and must be a number');
}
if (!Number.isFinite(input.totalThoughts)) {
throw new ValidationError('totalThoughts must be a finite number');
}
if (!Number.isInteger(input.totalThoughts)) {
throw new ValidationError('totalThoughts must be an integer');
}
if (input.totalThoughts < VALIDATION_LIMITS.MIN_TOTAL_THOUGHTS) {
throw new ValidationError(
`totalThoughts must be at least ${VALIDATION_LIMITS.MIN_TOTAL_THOUGHTS}`
);
}
if (input.totalThoughts > VALIDATION_LIMITS.MAX_TOTAL_THOUGHTS) {
throw new ValidationError(
`totalThoughts cannot exceed ${VALIDATION_LIMITS.MAX_TOTAL_THOUGHTS}`
);
}
// Validate thoughtNumber vs totalThoughts
if (input.thoughtNumber > input.totalThoughts) {
throw new ValidationError('thoughtNumber cannot exceed totalThoughts');
}
// Validate revision fields
if (input.isRevision === true && input.revisesThought === undefined) {
throw new ValidationError('revisesThought is required when isRevision is true');
}
if (input.revisesThought !== undefined) {
if (typeof input.revisesThought !== 'number') {
throw new ValidationError('revisesThought must be a number');
}
if (!Number.isFinite(input.revisesThought) || !Number.isInteger(input.revisesThought)) {
throw new ValidationError('revisesThought must be a finite integer');
}
if (input.revisesThought < VALIDATION_LIMITS.MIN_THOUGHT_NUMBER) {
throw new ValidationError('revisesThought must be a positive integer');
}
}
// Validate branch fields
if (input.branchId !== undefined) {
if (typeof input.branchId !== 'string') {
throw new ValidationError('branchId must be a string');
}
if (input.branchId.trim().length === 0) {
throw new ValidationError('branchId cannot be empty');
}
if (input.branchId.length > VALIDATION_LIMITS.MAX_BRANCH_ID_LENGTH) {
throw new ValidationError(
`branchId cannot exceed ${VALIDATION_LIMITS.MAX_BRANCH_ID_LENGTH} characters`
);
}
if (input.branchFromThought === undefined) {
throw new ValidationError('branchFromThought is required when branchId is specified');
}
}
if (input.branchFromThought !== undefined) {
if (typeof input.branchFromThought !== 'number') {
throw new ValidationError('branchFromThought must be a number');
}
if (!Number.isFinite(input.branchFromThought) || !Number.isInteger(input.branchFromThought)) {
throw new ValidationError('branchFromThought must be a finite integer');
}
if (input.branchFromThought < VALIDATION_LIMITS.MIN_THOUGHT_NUMBER) {
throw new ValidationError('branchFromThought must be a positive integer');
}
}
}