log-tool-utils.ts•4.18 kB
import { ToolArguments } from '../core/handlers/base-handler.js';
import { SFCCLogClient } from '../clients/log-client.js';
import { LogLevel, isValidLogLevel } from './log-tool-constants.js';
/**
 * Configuration interface for tool dispatch
 */
export interface ToolSpec<T = any> {
  validate?: (args: ToolArguments, toolName: string) => void;
  defaults?: (args: ToolArguments) => Record<string, any>;
  exec: (args: ToolArguments, client: SFCCLogClient) => Promise<T>;
  logMessage: (args: ToolArguments) => string;
}
/**
 * Validation utilities for log tools
 */
export class LogToolValidators {
  static validateLogLevel(level: string, toolName: string): void {
    if (!isValidLogLevel(level)) {
      throw new Error(`Invalid log level '${level}' for ${toolName}. Valid levels: ${Object.values(LogLevel).join(', ')}`);
    }
  }
  static validateLimit(limit: number | undefined, toolName: string): void {
    if (limit !== undefined) {
      // Validate type first
      if (typeof limit !== 'number' || isNaN(limit)) {
        throw new Error(`Invalid limit '${limit}' for ${toolName}. Must be a valid number`);
      }
      // Then validate range
      if (limit <= 0 || limit > 1000) {
        throw new Error(`Invalid limit '${limit}' for ${toolName}. Must be between 1 and 1000`);
      }
    }
  }
  static validateMaxBytes(maxBytes: number | undefined, toolName: string): void {
    if (maxBytes !== undefined) {
      // Validate type first
      if (typeof maxBytes !== 'number' || isNaN(maxBytes)) {
        throw new Error(`Invalid maxBytes '${maxBytes}' for ${toolName}. Must be a valid number`);
      }
      // Then validate range
      if (maxBytes <= 0 || maxBytes > 10_000_000) { // 10MB limit
        throw new Error(`Invalid maxBytes '${maxBytes}' for ${toolName}. Must be between 1 and 10,000,000`);
      }
    }
  }
  static validateFilename(filename: string, toolName: string): void {
    if (!filename || filename.trim().length === 0) {
      throw new Error(`Filename is required for ${toolName}`);
    }
    // Prevent path traversal
    if (filename.includes('..') || filename.includes('\\')) {
      throw new Error(`Invalid filename '${filename}' for ${toolName}. Path traversal not allowed`);
    }
  }
}
/**
 * Message formatting utilities for log tools
 */
export class LogMessageFormatter {
  static formatLatestLogs(level: string, limit: number, date?: string): string {
    return `Fetching latest ${level} logs limit=${limit} date=${date ?? 'today'}`;
  }
  static formatSummarizeLogs(date?: string): string {
    return `Summarizing logs for date ${date ?? 'today'}`;
  }
  static formatSearchLogs(pattern: string, logLevel?: string, limit?: number, _date?: string): string {
    // For backward compatibility with existing tests, don't include date in message
    return `Searching logs pattern="${pattern}" level=${logLevel ?? 'all'} limit=${limit ?? 20}`;
  }
  static formatListLogFiles(): string {
    return 'Listing log files';
  }
  static formatGetLogFileContents(filename: string, maxBytes?: number, tailOnly?: boolean): string {
    return `Reading log file contents: ${filename} (maxBytes=${maxBytes ?? 'default'}, tailOnly=${tailOnly ?? false})`;
  }
  static formatJobLogMessage(operation: string, params: Record<string, any>): string {
    const paramStr = Object.entries(params)
      .filter(([_, value]) => value !== undefined)
      .map(([key, value]) => `${key}=${value}`)
      .join(' ');
    return paramStr ? `${operation} ${paramStr}` : operation;
  }
}
/**
 * Utility functions for argument processing
 */
export class LogToolUtils {
  static applyDefaults(spec: ToolSpec, args: ToolArguments): ToolArguments {
    if (!spec.defaults) {
      return args;
    }
    const defaults = spec.defaults(args);
    return { ...args, ...defaults };
  }
  static createValidatedArgs(spec: ToolSpec, args: ToolArguments, toolName: string): ToolArguments {
    // Apply defaults first
    const processedArgs = LogToolUtils.applyDefaults(spec, args);
    // Validate if validator exists
    if (spec.validate) {
      spec.validate(processedArgs, toolName);
    }
    return processedArgs;
  }
}