Persistent configuration and caching system with automatic cleanup and workflow-specific data management. Alfy provides three storage mechanisms: workflow configuration, user configuration, and cached data with TTL support.
Persistent storage for workflow-specific configuration data using the conf library.
/**
* Workflow configuration storage
* Conf instance with workflow-specific data directory
*/
config: Conf;Usage Examples:
import alfy from "alfy";
// Store configuration values
alfy.config.set('apiKey', 'your-api-key-here');
alfy.config.set('theme', 'dark');
alfy.config.set('maxResults', 10);
// Store complex objects
alfy.config.set('preferences', {
notifications: true,
autoUpdate: false,
language: 'en'
});
// Retrieve configuration values
const apiKey = alfy.config.get('apiKey');
const theme = alfy.config.get('theme', 'light'); // With default value
const maxResults = alfy.config.get('maxResults');
console.log(`Using theme: ${theme}, showing ${maxResults} results`);
// Check if configuration exists
if (alfy.config.has('apiKey')) {
// Use API key...
} else {
// Prompt user to configure API key...
}
// Remove configuration
alfy.config.delete('oldSetting');
// Clear all configuration
alfy.config.clear();Access to user-defined workflow configuration variables set through Alfred's workflow configuration interface.
/**
* User workflow configuration
* Map instance containing user-configured values
*/
userConfig: Map<string, string>;Usage Examples:
import alfy from "alfy";
// Get user-configured API token
const apiToken = alfy.userConfig.get('apiToken');
if (!apiToken) {
alfy.error('Please configure your API token in the workflow settings');
return;
}
// Get optional configuration with defaults
const baseUrl = alfy.userConfig.get('baseUrl') || 'https://api.example.com';
const timeout = parseInt(alfy.userConfig.get('timeout') || '5000');
// Check if user has configured specific options
if (alfy.userConfig.has('customEndpoint')) {
const endpoint = alfy.userConfig.get('customEndpoint');
console.log(`Using custom endpoint: ${endpoint}`);
}
// Iterate through all user configuration
for (const [key, value] of alfy.userConfig) {
console.log(`User config ${key}: ${value}`);
}
// Use in API requests
const response = await alfy.fetch(`${baseUrl}/data`, {
headers: {
'Authorization': `Bearer ${apiToken}`,
'User-Agent': alfy.userConfig.get('userAgent') || 'Alfy Workflow'
},
timeout: { request: timeout }
});High-performance caching with TTL (time-to-live) support and automatic cleanup.
/**
* Cache storage with TTL support
* CacheConf instance extending Conf with cache-specific methods
*/
cache: CacheConf;
interface CacheConf extends Conf {
/** Check if a cached item has expired */
isExpired(key: string): boolean;
/** Get cached value with optional default and maxAge override */
get<T>(key: string, defaultValue?: T, options?: CacheGetOptions): T;
/** Set cached value with optional TTL */
set(key: string, value: unknown, options?: CacheSetOptions): void;
}
interface CacheGetOptions {
/** Ignore maxAge and return expired items */
ignoreMaxAge?: boolean;
}
interface CacheSetOptions {
/** Number of milliseconds the cached value is valid */
maxAge?: number;
}Basic Caching:
import alfy from "alfy";
// Store data in cache
alfy.cache.set('userList', users);
alfy.cache.set('lastUpdate', Date.now());
// Retrieve cached data
const cachedUsers = alfy.cache.get('userList');
const lastUpdate = alfy.cache.get('lastUpdate', 0); // With default
if (cachedUsers) {
console.log(`Found ${cachedUsers.length} cached users`);
} else {
console.log('No cached users found');
}TTL Caching:
import alfy from "alfy";
// Cache with 5-minute expiration
alfy.cache.set('apiResponse', data, {
maxAge: 5 * 60 * 1000 // 5 minutes in milliseconds
});
// Cache with 1-hour expiration
alfy.cache.set('configuration', config, {
maxAge: 60 * 60 * 1000 // 1 hour
});
// Check if cache has expired
if (alfy.cache.isExpired('apiResponse')) {
console.log('API response cache has expired');
// Fetch fresh data...
} else {
const cached = alfy.cache.get('apiResponse');
console.log('Using cached API response');
}
// Get cached data even if expired
const staleData = alfy.cache.get('apiResponse', null, {
ignoreMaxAge: true
});Advanced Caching Patterns:
import alfy from "alfy";
// Cache with fallback and refresh pattern
async function getCachedData(cacheKey, fetchFunction, maxAge = 60000) {
// Try to get cached data
if (!alfy.cache.isExpired(cacheKey)) {
return alfy.cache.get(cacheKey);
}
try {
// Fetch fresh data
const freshData = await fetchFunction();
// Cache the fresh data
alfy.cache.set(cacheKey, freshData, { maxAge });
return freshData;
} catch (error) {
// Return stale cached data if fetch fails
const staleData = alfy.cache.get(cacheKey, null, { ignoreMaxAge: true });
if (staleData) {
console.log('Using stale cached data due to fetch error');
return staleData;
}
throw error;
}
}
// Usage
const userData = await getCachedData(
'users',
() => alfy.fetch('https://api.example.com/users'),
10 * 60 * 1000 // 10 minutes
);Cache Management:
import alfy from "alfy";
// Clear specific cache entries
alfy.cache.delete('expiredData');
// Clear all cache
alfy.cache.clear();
// Get cache statistics
const cacheSize = alfy.cache.size;
console.log(`Cache contains ${cacheSize} items`);
// Check what's in cache
for (const [key, value] of alfy.cache) {
const expired = alfy.cache.isExpired(key);
console.log(`Cache key "${key}": ${expired ? 'expired' : 'valid'}`);
}
// Cleanup expired entries manually (usually automatic)
const allKeys = [...alfy.cache.keys()];
for (const key of allKeys) {
if (alfy.cache.isExpired(key)) {
alfy.cache.delete(key);
}
}All storage uses Alfred's recommended directories:
~/Library/Application Support/Alfred/Workflow Data/{bundle-id}/~/Library/Caches/com.runningwithcrayons.Alfred/Workflow Data/{bundle-id}/Alfy automatically handles:
Configuration Storage:
import alfy from "alfy";
// Use hierarchical configuration
alfy.config.set('api.endpoints.primary', 'https://api.example.com');
alfy.config.set('api.endpoints.fallback', 'https://backup.example.com');
alfy.config.set('ui.theme', 'dark');
alfy.config.set('ui.maxResults', 20);
// Validate configuration on startup
function validateConfig() {
const required = ['api.token', 'api.endpoints.primary'];
for (const key of required) {
if (!alfy.config.has(key)) {
throw new Error(`Missing required configuration: ${key}`);
}
}
}Smart Caching:
import alfy from "alfy";
// Cache different data with appropriate TTLs
const CACHE_DURATIONS = {
userProfile: 60 * 60 * 1000, // 1 hour - relatively stable
searchResults: 5 * 60 * 1000, // 5 minutes - can change frequently
configuration: 24 * 60 * 60 * 1000, // 24 hours - very stable
realtimeData: 30 * 1000 // 30 seconds - highly volatile
};
// Use structured cache keys
function cacheKey(type, ...parts) {
return `${type}:${parts.join(':')}`;
}
// Cache search results with user-specific keys
const searchKey = cacheKey('search', alfy.input, 'page1');
alfy.cache.set(searchKey, results, { maxAge: CACHE_DURATIONS.searchResults });