@satoshibits/cache-keys
Version:
Type-safe cache key generation with zero dependencies
162 lines • 5.28 kB
JavaScript
;
import { sanitizeKeyComponent } from './sanitizer.mjs';
/**
* Create an invalidation pattern from a key factory and partial arguments
*
* @param factory The key factory to use
* @param prefixArgs Partial arguments to create a prefix pattern
* @returns A pattern string for matching keys
*
* @example
* // Invalidate all campaign keys for an account
* const pattern = createInvalidationPattern(campaignKeys.list, accountId);
* // Returns: "campaign:list:507f1f77bcf86cd799439011:*"
*/
export function createInvalidationPattern(factory) {
var prefixArgs = [];
for (var _i = 1; _i < arguments.length; _i++) {
prefixArgs[_i - 1] = arguments[_i];
}
if (prefixArgs.length === 0) {
return factory.pattern();
}
// Get the factory pattern and extract the prefix
var fullPattern = factory.pattern();
var prefix = fullPattern.substring(0, fullPattern.lastIndexOf(':') + 1);
// Sanitize all arguments to prevent injection
var prefixParts = prefixArgs.map(function (arg) { return sanitizeKeyComponent(String(arg)); });
return prefix + prefixParts.join(':') + ':*';
}
/**
* Match keys against a pattern
*
* @param keys Array of cache keys
* @param pattern Pattern to match against (supports * wildcard)
* @returns Array of matching keys
*/
export function matchKeys(keys, pattern) {
// Convert pattern to regex
var regexPattern = pattern
.split('*')
.map(function (part) { return escapeRegex(part); })
.join('.*');
var regex = new RegExp("^".concat(regexPattern, "$"));
return keys.filter(function (key) { return regex.test(key); });
}
/**
* Escape special regex characters in a string
*/
function escapeRegex(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/**
* Create a pattern that matches any of the given prefixes
*
* @param prefixes Array of key prefixes
* @returns A regex pattern for matching
*/
export function createMultiPattern(prefixes) {
var escapedPrefixes = prefixes.map(escapeRegex);
return new RegExp("^(".concat(escapedPrefixes.join('|'), ")"));
}
/**
* Extract the namespace from a cache key
*
* @param key The cache key
* @param separator The separator used in the key
* @returns The namespace or null if invalid
*/
export function extractNamespace(key, separator) {
if (separator === void 0) { separator = ':'; }
var firstSeparatorIndex = key.indexOf(separator);
if (firstSeparatorIndex === -1) {
return null;
}
return key.substring(0, firstSeparatorIndex);
}
/**
* Extract namespace and type from a cache key
*
* @param key The cache key
* @param separator The separator used in the key
* @returns Object with namespace and type or null if invalid
*/
export function extractKeyType(key, separator) {
var _a, _b;
if (separator === void 0) { separator = ':'; }
var parts = key.split(separator);
if (parts.length < 2) {
return null;
}
return {
namespace: (_a = parts[0]) !== null && _a !== void 0 ? _a : '',
type: (_b = parts[1]) !== null && _b !== void 0 ? _b : '',
};
}
/**
* Group keys by their namespace
*
* @param keys Array of cache keys
* @param separator The separator used in keys
* @returns Map of namespace to keys
*/
export function groupKeysByNamespace(keys, separator) {
var _a;
if (separator === void 0) { separator = ':'; }
var groups = new Map();
for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
var key = keys_1[_i];
var namespace = extractNamespace(key, separator);
if (namespace) {
var group = (_a = groups.get(namespace)) !== null && _a !== void 0 ? _a : [];
group.push(key);
groups.set(namespace, group);
}
}
return groups;
}
/**
* Create a hierarchical pattern for invalidation
* Useful for invalidating parent and child keys together
*
* @param parts Hierarchical parts of the key
* @param separator The separator to use
* @returns Array of patterns from most specific to least specific
*
* @example
* createHierarchicalPatterns(['user', '123', 'posts'])
* // Returns: ['user:123:posts:*', 'user:123:*', 'user:*']
*/
export function createHierarchicalPatterns(parts, separator) {
if (separator === void 0) { separator = ':'; }
var patterns = [];
for (var i = parts.length; i > 0; i--) {
var prefix = parts.slice(0, i).join(separator);
patterns.push("".concat(prefix).concat(separator, "*"));
}
return patterns;
}
/**
* Check if a key matches any of the provided patterns
*
* @param key The key to check
* @param patterns Array of patterns to match against
* @returns true if the key matches any pattern
*/
export function matchesAnyPattern(key, patterns) {
if (patterns.length === 0) {
return false;
}
// Compile all patterns into a single regex for efficiency
var megaPattern = patterns
.map(function (pattern) {
return pattern
.split('*')
.map(function (part) { return escapeRegex(part); })
.join('.*');
})
.join('|');
var regex = new RegExp("^(".concat(megaPattern, ")$"));
return regex.test(key);
}
//# sourceMappingURL=patterns.mjs.map