Blazing fast memoization library for JavaScript with comprehensive configuration options and React support
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Methods for transforming and manipulating function arguments before caching, enabling advanced cache key customization and conditional cache updates.
Transform arguments before they are used to generate cache keys, allowing for normalization and custom cache key generation.
/**
* Transform arguments before caching
* @param transformer Function to transform the argument array (cache key)
* @returns Moizer with argument transformation
*/
transformArgs<Transformer extends TransformKey>(
transformer: Transformer
): Moizer<{ transformArgs: Transformer }>;
type TransformKey = (key: Key) => Key;
type Key<Arg extends any = any> = Arg[];Usage Examples:
import moize from "moize";
// Normalize string arguments
const normalizeStrings = (key: any[]) => {
return key.map(arg =>
typeof arg === 'string' ? arg.toLowerCase().trim() : arg
);
};
const processText = (text: string, options: { uppercase: boolean }) => {
return options.uppercase ? text.toUpperCase() : text.toLowerCase();
};
const normalizedMemoized = moize.transformArgs(normalizeStrings)(processText);
console.log(normalizedMemoized(" Hello ", { uppercase: true })); // Computed
console.log(normalizedMemoized("hello", { uppercase: true })); // Cached (normalized match)
// Sort array arguments for order-independent caching
const sortArrayArgs = (key: any[]) => {
return key.map(arg => {
if (Array.isArray(arg)) {
return [...arg].sort();
}
return arg;
});
};
const sumArray = (numbers: number[]) => numbers.reduce((a, b) => a + b, 0);
const orderIndependentSum = moize.transformArgs(sortArrayArgs)(sumArray);
console.log(orderIndependentSum([3, 1, 2])); // 6 (computed)
console.log(orderIndependentSum([1, 2, 3])); // 6 (cached - same sorted values)
// Extract specific properties for cache key
const extractUserKey = (key: any[]) => {
return key.map(arg => {
if (arg && typeof arg === 'object' && 'id' in arg && 'role' in arg) {
return { id: arg.id, role: arg.role }; // Only use id and role for caching
}
return arg;
});
};
const processUser = (user: {
id: string;
name: string;
email: string;
role: string;
lastLogin?: Date;
}) => {
return `${user.role.toUpperCase()}: ${user.name}`;
};
const userMemoized = moize.transformArgs(extractUserKey)(processUser);
const user1 = { id: "1", name: "Alice", email: "alice@example.com", role: "admin", lastLogin: new Date() };
const user2 = { id: "1", name: "Alice", email: "alice@newdomain.com", role: "admin" }; // Different email, no lastLogin
console.log(userMemoized(user1)); // Computed
console.log(userMemoized(user2)); // Cached (same id and role)Control when cache entries should be updated based on the current cache key.
/**
* Conditionally update cache based on key analysis
* @param updateCacheForKey Function that determines when to update cache
* @returns Moizer with conditional cache updating
*/
updateCacheForKey<UpdateWhen extends UpdateCacheForKey>(
updateCacheForKey: UpdateWhen
): Moizer<{ updateCacheForKey: UpdateWhen }>;
type UpdateCacheForKey = (key: Key) => boolean;Usage Examples:
import moize from "moize";
// Update cache only for specific conditions
const shouldUpdateCache = (key: any[]) => {
const [data, options] = key;
// Always update if force refresh is requested
if (options && options.forceRefresh) {
return true;
}
// Update if data is marked as stale
if (data && data.lastModified) {
const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
return data.lastModified > fiveMinutesAgo;
}
return false; // Use cached version otherwise
};
const processData = (
data: { content: any; lastModified?: number },
options: { forceRefresh?: boolean } = {}
) => {
console.log('Processing data...');
return { processed: data.content, timestamp: Date.now() };
};
const conditionalMemoized = moize.updateCacheForKey(shouldUpdateCache)(processData);
const oldData = { content: "test", lastModified: Date.now() - 10 * 60 * 1000 }; // 10 min ago
const freshData = { content: "test", lastModified: Date.now() }; // Now
console.log(conditionalMemoized(oldData)); // Uses cache if available
console.log(conditionalMemoized(freshData)); // Always recomputes (fresh data)
console.log(conditionalMemoized(oldData, { forceRefresh: true })); // Always recomputes (forced)
// Version-based cache updating
const versionBasedUpdate = (key: any[]) => {
const [resource] = key;
if (!resource || typeof resource.version !== 'number') {
return true; // Update if no version info
}
// Update if version is different from what might be cached
return true; // In practice, you'd compare with cached version
};
const fetchResource = (resource: { id: string; version: number }) => {
console.log(`Fetching resource ${resource.id} v${resource.version}`);
return { data: `Resource ${resource.id}`, version: resource.version };
};
const versionMemoized = moize.updateCacheForKey(versionBasedUpdate)(fetchResource);
// Time-based cache invalidation
const timeBasedUpdate = (key: any[]) => {
const [, options] = key;
if (options && options.timestamp) {
const thirtySecondsAgo = Date.now() - 30 * 1000;
return options.timestamp > thirtySecondsAgo; // Update if timestamp is recent
}
return false;
};
const timestampMemoized = moize.updateCacheForKey(timeBasedUpdate)(processData);Argument transformation methods can be combined with other moize features for sophisticated caching strategies.
import moize from "moize";
// Normalize and extract key properties
const normalizeAndExtract = (key: any[]) => {
return key.map(arg => {
if (typeof arg === 'string') {
return arg.toLowerCase().trim();
}
if (arg && typeof arg === 'object') {
// Extract only relevant properties
const { id, type, status } = arg;
return { id, type, status };
}
return arg;
});
};
// Update based on data freshness
const updateForFreshData = (key: any[]) => {
const [item] = key;
if (item && item.updatedAt) {
const oneHourAgo = Date.now() - 60 * 60 * 1000;
return item.updatedAt > oneHourAgo;
}
return true; // Update if no timestamp
};
const processItem = (item: {
id: string;
type: string;
status: string;
data: any;
updatedAt?: number;
metadata?: any;
}) => {
return {
processedId: item.id,
processedType: item.type.toUpperCase(),
processedStatus: item.status,
processedAt: Date.now()
};
};
const advancedMemoized = moize
.transformArgs(normalizeAndExtract) // Normalize cache keys
.updateCacheForKey(updateForFreshData) // Update for fresh data
.maxSize(50) // Limit cache size
.maxAge(3600000) // 1 hour TTL
.profile('item-processing') // Performance monitoring
(processItem);
// Chaining with other specialized methods
const apiProcessor = async (endpoint: string, params: Record<string, any>) => {
const response = await fetch(endpoint, {
method: 'POST',
body: JSON.stringify(params),
headers: { 'Content-Type': 'application/json' }
});
return response.json();
};
const normalizeApiArgs = (key: any[]) => {
const [endpoint, params] = key;
return [
endpoint.toLowerCase(),
params ? Object.keys(params).sort().reduce((sorted, k) => {
sorted[k] = params[k];
return sorted;
}, {} as any) : {}
];
};
const smartApiMemoized = moize.promise
.transformArgs(normalizeApiArgs)
.serialize
.maxAge(60000)
(apiProcessor);updateCacheForKey functions should be fast to avoid performance penalties