Skip to main content
Glama
utils.ts4.69 kB
// ============================================================================= // kivv - Shared Utility Functions // ============================================================================= // Workers-compatible utilities (NO Node.js APIs) // Uses crypto.subtle and crypto.randomUUID() only // ============================================================================= /** * Generate SHA-256 hash of text (for content deduplication) * Uses Workers crypto.subtle API (NOT Node.js crypto) * * @param text - The text to hash * @returns Hex-encoded SHA-256 hash * * @example * const hash = await hashContent("Machine Learning Paper Title..."); * // Returns: "a3f2b9c8d..." */ export async function hashContent(text: string): Promise<string> { const encoder = new TextEncoder(); const data = encoder.encode(text); const hashBuffer = await crypto.subtle.digest('SHA-256', data); const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); return hashHex; } /** * Generate UUID v4 (for API keys, request IDs) * Uses Workers crypto.randomUUID() (NOT Node.js crypto) * * @returns UUID v4 string * * @example * const apiKey = generateId(); * // Returns: "f47ac10b-58cc-4372-a567-0e02b2c3d479" */ export function generateId(): string { return crypto.randomUUID(); } /** * Parse JSON array from SQLite TEXT field safely * Authors and categories are stored as JSON strings in D1 * * @param jsonString - JSON-encoded array string * @returns Parsed array or empty array on error * * @example * const authors = parseJsonArray<string>('["Alice", "Bob"]'); * // Returns: ["Alice", "Bob"] * * const invalid = parseJsonArray<string>('not json'); * // Returns: [] */ export function parseJsonArray<T>(jsonString: string): T[] { try { const parsed = JSON.parse(jsonString); return Array.isArray(parsed) ? parsed : []; } catch { return []; } } /** * Format date to YYYY-MM-DD (for SQLite DATE queries) * * @param date - Date object to format * @returns ISO date string (YYYY-MM-DD) * * @example * const today = formatDate(new Date()); * // Returns: "2025-11-30" */ export function formatDate(date: Date): string { return date.toISOString().split('T')[0]; } /** * Calculate cost in USD based on token usage * From PRD: Haiku $0.25/1M input, $1.25/1M output * Sonnet $3/1M input, $15/1M output * * @param tokens - Number of tokens * @param model - Model and direction ('haiku-input' | 'haiku-output' | 'sonnet-input' | 'sonnet-output') * @returns Cost in USD * * @example * const cost = calculateCost(1000, 'haiku-input'); * // Returns: 0.00025 */ export function calculateCost( tokens: number, model: 'haiku-input' | 'haiku-output' | 'sonnet-input' | 'sonnet-output' ): number { const rates = { 'haiku-input': 0.25 / 1_000_000, 'haiku-output': 1.25 / 1_000_000, 'sonnet-input': 3.0 / 1_000_000, 'sonnet-output': 15.0 / 1_000_000, }; return tokens * rates[model]; } /** * Create standardized API error response * Used by MCP server to return consistent error format * * @param error - Human-readable error message * @param code - Machine-readable error code * @param status - HTTP status code * @param details - Optional additional error details * @returns Response object with JSON error body * * @example * return createErrorResponse( * 'Invalid API key', * 'INVALID_API_KEY', * 401, * { provided_key: 'abc***' } * ); */ export function createErrorResponse( error: string, code: string, status: number, details?: Record<string, unknown> ): Response { return new Response( JSON.stringify({ error, code, details }), { status, headers: { 'Content-Type': 'application/json' }, } ); } /** * Validate and sanitize arXiv ID format * arXiv ID format: YYMM.NNNNN or YYMM.NNNNNN or arXiv:YYMM.NNNNN * * @param id - arXiv ID to validate * @returns True if valid arXiv ID format * * @example * validateArxivId('2311.12345'); // true * validateArxivId('arXiv:2311.12345'); // true * validateArxivId('invalid'); // false */ export function validateArxivId(id: string): boolean { // arXiv ID format: YYMM.NNNNN or YYMM.NNNNNN or arXiv:YYMM.NNNNN const pattern = /^(arxiv:)?(\d{4}\.\d{4,6})$/i; return pattern.test(id); } /** * Sleep/delay utility (for rate limiting, retries) * * @param ms - Milliseconds to sleep * @returns Promise that resolves after delay * * @example * await sleep(3000); // Wait 3 seconds */ export function sleep(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jeffaf/kivv'

If you have feedback or need assistance with the MCP directory API, please join our Discord server