UNPKG

@sethdouglasford/claude-flow

Version:

Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology

485 lines 13.2 kB
/** * Utility helper functions for Claude-Flow */ // Utility helper functions /** * Simple calculator function that adds two numbers */ export function add(a, b) { return a + b; } /** * Simple hello world function */ export function helloWorld() { return "Hello, World!"; } /** * Generates a unique identifier */ export function generateId(prefix) { const timestamp = Date.now().toString(36); const random = Math.random().toString(36).substr(2, 9); return prefix ? `${prefix}_${timestamp}_${random}` : `${timestamp}_${random}`; } /** * Creates a timeout promise that rejects after the specified time */ export function timeout(promise, ms, message) { let timeoutId; let completed = false; const timeoutPromise = new Promise((_, reject) => { timeoutId = setTimeout(() => { if (!completed) { completed = true; reject(new Error(message ?? "Operation timed out")); } }, ms); }); const wrappedPromise = promise.then((result) => { completed = true; if (timeoutId !== undefined) { clearTimeout(timeoutId); } return result; }, (error) => { completed = true; if (timeoutId !== undefined) { clearTimeout(timeoutId); } throw error; }); return Promise.race([ wrappedPromise, timeoutPromise, ]); } /** * Delays execution for specified milliseconds */ export function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } /** * Retries a function with exponential backoff */ export async function retry(fn, options = {}) { const { maxAttempts = 3, initialDelay = 1000, maxDelay = 30000, factor = 2, onRetry, } = options; let lastError; let delayMs = initialDelay; for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); if (attempt === maxAttempts) { throw lastError; } if (onRetry) { onRetry(attempt, lastError); } await delay(Math.min(delayMs, maxDelay)); delayMs *= factor; } } // lastError should be defined here, but provide fallback throw lastError ?? new Error("Operation failed after maximum attempts"); } /** * Debounces a function */ export function debounce(fn, delayMs) { let timeoutId; return (...args) => { if (timeoutId !== undefined) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { fn(...args); timeoutId = undefined; }, delayMs); }; } /** * Throttles a function */ export function throttle(fn, limitMs) { let inThrottle = false; let lastArgs = null; return (...args) => { if (!inThrottle) { fn(...args); inThrottle = true; setTimeout(() => { inThrottle = false; if (lastArgs !== null) { fn(...lastArgs); lastArgs = null; } }, limitMs); } else { lastArgs = args; } }; } /** * Deep clones an object */ export function deepClone(obj, visited = new WeakMap()) { if (obj === null || typeof obj !== "object") { return obj; } // Check for circular reference if (visited.has(obj)) { return visited.get(obj); } if (obj instanceof Date) { return new Date(obj.getTime()); } if (obj instanceof Array) { const clonedArray = []; visited.set(obj, clonedArray); obj.forEach((item, index) => { clonedArray[index] = deepClone(item, visited); }); return clonedArray; } if (obj instanceof Map) { const map = new Map(); visited.set(obj, map); obj.forEach((value, key) => { map.set(key, deepClone(value, visited)); }); return map; } if (obj instanceof Set) { const set = new Set(); visited.set(obj, set); obj.forEach((value) => { set.add(deepClone(value, visited)); }); return set; } const cloned = {}; visited.set(obj, cloned); for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { cloned[key] = deepClone(obj[key], visited); } } return cloned; } /** * Merges multiple objects deeply */ export function deepMerge(target, ...sources) { // Create a deep clone of the target to avoid mutation const result = deepClone(target); if (!sources.length) return result; const source = sources.shift(); if (!source) return result; for (const key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { const sourceValue = source[key]; const resultValue = result[key]; if (isObject(resultValue) && isObject(sourceValue)) { result[key] = deepMerge(resultValue, sourceValue); } else { result[key] = sourceValue; } } } return deepMerge(result, ...sources); } /** * Checks if a value is a plain object */ function isObject(value) { return value !== null && typeof value === "object" && !Array.isArray(value); } /** * Creates a typed event emitter */ export class TypedEventEmitter { listeners = new Map(); on(event, handler) { if (!this.listeners.has(event)) { this.listeners.set(event, new Set()); } const handlers = this.listeners.get(event); if (handlers) { handlers.add(handler); } } off(event, handler) { const handlers = this.listeners.get(event); if (handlers) { handlers.delete(handler); } } emit(event, data) { const handlers = this.listeners.get(event); if (handlers) { handlers.forEach((handler) => handler(data)); } } once(event, handler) { const onceHandler = (data) => { handler(data); this.off(event, onceHandler); }; this.on(event, onceHandler); } removeAllListeners(event) { if (event) { this.listeners.delete(event); } else { this.listeners.clear(); } } } /** * Formats bytes to human-readable string */ export function formatBytes(bytes, decimals = 2) { if (bytes === 0) return "0 Bytes"; const k = 1024; const dm = decimals < 0 ? 0 : decimals; const sizes = ["Bytes", "KB", "MB", "GB", "TB"]; // Handle negative numbers const absBytes = Math.abs(bytes); const i = Math.floor(Math.log(absBytes) / Math.log(k)); const value = parseFloat((absBytes / Math.pow(k, i)).toFixed(dm)); const sign = bytes < 0 ? "-" : ""; return `${sign + value} ${sizes[i]}`; } /** * Parses duration string to milliseconds */ export function parseDuration(duration) { const match = duration.match(/^(\d+)(ms|s|m|h|d)$/); if (!match) { throw new Error(`Invalid duration format: ${duration}`); } const value = parseInt(match[1], 10); const unit = match[2]; switch (unit) { case "ms": return value; case "s": return value * 1000; case "m": return value * 60 * 1000; case "h": return value * 60 * 60 * 1000; case "d": return value * 24 * 60 * 60 * 1000; default: throw new Error(`Unknown duration unit: ${unit}`); } } /** * Ensures a value is an array */ export function ensureArray(value) { return Array.isArray(value) ? value : [value]; } /** * Groups an array by a key function */ export function groupBy(items, keyFn) { return items.reduce((groups, item) => { const key = keyFn(item); if (!groups[key]) { groups[key] = []; } groups[key].push(item); return groups; }, {}); } /** * Creates a promise that can be resolved/rejected externally */ export function createDeferred() { let resolve; let reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); // Type assertion is safe here because the Promise constructor runs synchronously return { promise, resolve: resolve, reject: reject, }; } /** * Safely parses JSON with error handling */ export function safeParseJSON(json, fallback) { try { return JSON.parse(json); } catch { return fallback; } } /** * Simple calculator function with basic operations */ export function calculator(a, b, operation) { switch (operation) { case "+": return a + b; case "-": return a - b; case "*": return a * b; case "/": if (b === 0) { throw new Error("Division by zero"); } return a / b; case "^": return Math.pow(a, b); case "%": if (b === 0) { throw new Error("Modulo by zero"); } return a % b; default: throw new Error(`Invalid operation: ${operation}`); } } /** * Calculates the factorial of a number * @param n - The number to calculate factorial for * @returns The factorial of n * @throws Error if n is negative or not an integer */ export function factorial(n) { if (n < 0) { throw new Error("Factorial is not defined for negative numbers"); } if (!Number.isInteger(n)) { throw new Error("Factorial is only defined for integers"); } if (n === 0 || n === 1) { return 1; } // Iterative approach for better performance and to avoid stack overflow let result = 1; for (let i = 2; i <= n; i++) { result *= i; } return result; } /** * Creates a circuit breaker */ export function circuitBreaker(name, options) { const state = { failureCount: 0, lastFailureTime: 0, state: "closed", }; const isOpen = () => { if (state.state === "open") { const now = Date.now(); if (now - state.lastFailureTime >= options.resetTimeout) { state.state = "half-open"; return false; } return true; } return false; }; const recordSuccess = () => { state.failureCount = 0; state.state = "closed"; }; const recordFailure = () => { state.failureCount++; state.lastFailureTime = Date.now(); if (state.failureCount >= options.threshold) { state.state = "open"; } }; return { async execute(fn) { if (isOpen()) { throw new Error(`Circuit breaker ${name} is open`); } try { const result = await timeout(fn(), options.timeout); recordSuccess(); return result; } catch (error) { recordFailure(); throw error; } }, getState() { return { ...state }; }, reset() { state.failureCount = 0; state.lastFailureTime = 0; state.state = "closed"; }, }; } /** * Greeting function that returns a personalized greeting */ export function greeting(name, options) { const opts = { timeOfDay: false, formal: false, locale: "en", ...options, }; // Determine time-based greeting const getTimeGreeting = () => { const hour = new Date().getHours(); if (hour < 12) return "Good morning"; if (hour < 17) return "Good afternoon"; if (hour < 21) return "Good evening"; return "Good night"; }; // Get greeting by locale const getLocaleGreeting = () => { const greetings = { en: { informal: "Hello", formal: "Greetings" }, es: { informal: "Hola", formal: "Saludos" }, fr: { informal: "Salut", formal: "Bonjour" }, de: { informal: "Hallo", formal: "Guten Tag" }, it: { informal: "Ciao", formal: "Salve" }, pt: { informal: "Olá", formal: "Saudações" }, ja: { informal: "こんにちは", formal: "ご挨拶" }, zh: { informal: "你好", formal: "您好" }, }; const localeGreeting = greetings[opts.locale] ?? greetings.en; return opts.formal ? localeGreeting.formal : localeGreeting.informal; }; // Build the greeting let greetingText = opts.timeOfDay ? getTimeGreeting() : getLocaleGreeting(); if (name) { greetingText += `, ${name}`; } greetingText += "!"; return greetingText; } //# sourceMappingURL=helpers.js.map