Built-in error handling with user-friendly display and debugging utilities for workflow development. Alfy provides automatic exception handling and comprehensive logging capabilities for efficient workflow development and debugging.
Send debug information to Alfred's workflow debugger console.
/**
* Log message to Alfred workflow debugger
* @param text - Message to log (appears in Alfred's debugger console)
*/
function log(text: string): void;Usage Examples:
import alfy from "alfy";
// Basic logging
alfy.log('Workflow started');
alfy.log(`User input: ${alfy.input}`);
// Structured logging
alfy.log(`Processing ${items.length} items`);
alfy.log(`Cache hit: ${cacheKey}`);
alfy.log(`API response time: ${responseTime}ms`);
// JSON logging for complex data
const debugData = {
input: alfy.input,
itemCount: items.length,
timestamp: new Date().toISOString()
};
alfy.log(JSON.stringify(debugData, null, 2));
// Conditional logging
if (alfy.debug) {
alfy.log('Debug mode: Detailed processing information');
alfy.log(`Memory usage: ${process.memoryUsage().heapUsed / 1024 / 1024} MB`);
}
// Step-by-step process logging
alfy.log('Step 1: Fetching data from API');
const data = await alfy.fetch(apiUrl);
alfy.log(`Step 2: Received ${data.length} records`);
const filtered = alfy.inputMatches(data, 'title');
alfy.log(`Step 3: Filtered to ${filtered.length} matching records`);
alfy.log('Step 4: Generating Alfred items');
const items = filtered.map(mapToAlfredItem);
alfy.log(`Step 5: Generated ${items.length} Alfred items`);Display user-friendly errors in Alfred with automatic error formatting and system information.
/**
* Display error in Alfred with debugging information
* @param error - Error object or error message string
*/
function error(error: Error | string): void;Error Display Examples:
import alfy from "alfy";
// Handle API errors
try {
const data = await alfy.fetch('https://api.example.com/data');
} catch (error) {
alfy.error(error);
return; // Exit workflow
}
// Handle validation errors
function validateInput(input) {
if (!input) {
alfy.error('Please provide some input');
return false;
}
if (input.length < 3) {
alfy.error('Input must be at least 3 characters long');
return false;
}
return true;
}
if (!validateInput(alfy.input)) {
return;
}
// Handle configuration errors
if (!alfy.userConfig.get('apiToken')) {
alfy.error('API token not configured. Please set your API token in workflow settings.');
return;
}
// Custom error messages
function processData(data) {
if (!Array.isArray(data)) {
alfy.error('Invalid data format: Expected array');
return null;
}
if (data.length === 0) {
alfy.error('No data available to process');
return null;
}
return data.map(processItem);
}
// Network error handling
async function fetchWithErrorHandling(url) {
try {
return await alfy.fetch(url, { timeout: { request: 5000 } });
} catch (error) {
if (error.code === 'ETIMEDOUT') {
alfy.error('Request timed out. Please check your internet connection.');
} else if (error.response?.statusCode === 404) {
alfy.error('API endpoint not found. Please check the configuration.');
} else if (error.response?.statusCode >= 500) {
alfy.error('Server error. Please try again later.');
} else {
alfy.error(`Network error: ${error.message}`);
}
return null;
}
}Alfy automatically handles uncaught exceptions and unhandled promise rejections.
import alfy from "alfy";
// These errors are automatically caught and displayed by Alfy:
// Uncaught exceptions
throw new Error('Something went wrong');
// Unhandled promise rejections
Promise.reject(new Error('Async operation failed'));
// Top-level async errors (no need to catch)
const data = await alfy.fetch('https://invalid-url.com/api');
// If this fails, Alfy automatically shows the error to the user
// Syntax errors and runtime errors are also handled
undefinedFunction(); // Automatically caught and displayedWhen an error occurs, Alfy displays comprehensive debugging information:
// Error display includes:
// - Error name and message
// - Clean stack trace (filtered for relevance)
// - Workflow information (name, version)
// - Alfred version
// - System information (platform, OS version)
// - Copy-friendly format for bug reports
// Example error output in Alfred:
// Title: "TypeError: Cannot read property 'length' of undefined"
// Subtitle: "Press ⌘L to see the full error and ⌘C to copy it."
//
// ⌘L shows full stack trace
// ⌘C copies formatted error report:
//
// ```
// TypeError: Cannot read property 'length' of undefined
// at processData (file:///path/to/workflow/index.js:25:15)
// at async main (file:///path/to/workflow/index.js:40:3)
// ```
//
// -
// My Workflow 1.2.0
// Alfred 5.0.3
// darwin 21.6.0Debug Logging Pattern:
import alfy from "alfy";
function createDebugLogger(prefix) {
return function(message, data = null) {
if (alfy.debug) {
const timestamp = new Date().toISOString();
alfy.log(`[${timestamp}] ${prefix}: ${message}`);
if (data !== null) {
alfy.log(JSON.stringify(data, null, 2));
}
}
};
}
const debug = createDebugLogger('MyWorkflow');
// Usage
debug('Starting data processing');
debug('API response received', { status: 200, items: 50 });
debug('Filtering complete', { filtered: 25 });Error Recovery Patterns:
import alfy from "alfy";
async function fetchWithFallback(primaryUrl, fallbackUrl) {
try {
alfy.log('Attempting primary API');
return await alfy.fetch(primaryUrl);
} catch (primaryError) {
alfy.log(`Primary API failed: ${primaryError.message}`);
try {
alfy.log('Attempting fallback API');
return await alfy.fetch(fallbackUrl);
} catch (fallbackError) {
alfy.log(`Fallback API failed: ${fallbackError.message}`);
// Try cache as last resort
const cached = alfy.cache.get('lastKnownData', null, { ignoreMaxAge: true });
if (cached) {
alfy.log('Using stale cached data');
return cached;
}
alfy.error('All data sources unavailable. Please try again later.');
return null;
}
}
}
// Graceful degradation
function gracefulProcessing(data) {
try {
return fullProcessing(data);
} catch (error) {
alfy.log(`Full processing failed: ${error.message}, trying simple processing`);
try {
return simpleProcessing(data);
} catch (simpleError) {
alfy.log(`Simple processing failed: ${simpleError.message}`);
alfy.error('Unable to process data. Please check your input.');
return [];
}
}
}Performance Monitoring:
import alfy from "alfy";
function withTiming(name, fn) {
return async (...args) => {
const start = Date.now();
try {
const result = await fn(...args);
const duration = Date.now() - start;
if (alfy.debug) {
alfy.log(`${name} completed in ${duration}ms`);
}
return result;
} catch (error) {
const duration = Date.now() - start;
alfy.log(`${name} failed after ${duration}ms: ${error.message}`);
throw error;
}
};
}
// Usage
const timedFetch = withTiming('API fetch', alfy.fetch);
const timedProcessing = withTiming('Data processing', processData);
const data = await timedFetch('https://api.example.com/data');
const results = await timedProcessing(data);Configuration Errors:
import alfy from "alfy";
function validateConfiguration() {
const requiredConfigs = ['apiToken', 'baseUrl'];
const missing = requiredConfigs.filter(key => !alfy.userConfig.has(key));
if (missing.length > 0) {
alfy.error(`Missing configuration: ${missing.join(', ')}. Please configure in workflow settings.`);
return false;
}
return true;
}
if (!validateConfiguration()) {
return;
}Data Validation Errors:
import alfy from "alfy";
function validateApiResponse(data) {
if (!data) {
alfy.error('No data received from API');
return false;
}
if (!Array.isArray(data.items)) {
alfy.error('Invalid API response format');
return false;
}
if (data.items.length === 0) {
alfy.error('No items found matching your query');
return false;
}
return true;
}
const response = await alfy.fetch(apiUrl);
if (!validateApiResponse(response)) {
return;
}User Input Errors:
import alfy from "alfy";
function validateUserInput(input) {
if (!input || input.trim().length === 0) {
alfy.error('Please enter a search term');
return false;
}
if (input.length < 2) {
alfy.error('Search term must be at least 2 characters');
return false;
}
if (!/^[a-zA-Z0-9\s-_]+$/.test(input)) {
alfy.error('Search term contains invalid characters');
return false;
}
return true;
}
if (!validateUserInput(alfy.input)) {
return;
}