key-file-storage
Version:
Simple key-value storage directly on file system, maps each key to a separate file.
187 lines (186 loc) • 8.05 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
function createCache(cacheConfig) {
if (cacheConfig === true || typeof cacheConfig === 'undefined') {
// Unlimited cache by default
return createCache_Unlimited(cacheConfig);
}
else if (cacheConfig === false) {
// No cache
return createCache_NoCache(cacheConfig);
}
else if (typeof cacheConfig === 'number' && cacheConfig > 0) {
// Limited cache by the number of keys
return createCache_LimitedByKeyCount(cacheConfig);
}
else {
throw new Error('Invalid cache config.');
}
function createCache_Unlimited(cacheConfig) {
var collectionCache = {};
return new Proxy({
/*CACHE*/
}, {
set: function (target, property, value, receiver) {
var propertyName = String(property);
if (propertyName.endsWith('/')) {
collectionCache[propertyName] = value;
return true;
}
target[propertyName] = value;
Object.keys(collectionCache)
.filter(function (collection) { return keyInCollection(propertyName, collection); })
.forEach(function (collection) {
return collectionCache[collection].includes(propertyName) || collectionCache[collection].push(propertyName);
});
return true;
},
get: function (target, property, receiver) {
var propertyName = String(property);
if (propertyName.endsWith('/'))
return collectionCache[propertyName];
return target[propertyName];
},
deleteProperty: function (target, property) {
var propertyName = String(property);
if (propertyName === '*') {
collectionCache = {};
Object.keys(target).forEach(function (key) { return delete target[key]; });
return true;
}
if (propertyName.endsWith('/'))
return delete collectionCache[propertyName];
Object.keys(collectionCache)
.filter(function (collection) { return keyInCollection(propertyName, collection); })
.forEach(function (collection) {
return collectionCache[collection].includes(propertyName) &&
collectionCache[collection].splice(collectionCache[collection].indexOf(propertyName), 1);
});
return delete target[propertyName];
},
has: function (target, property) {
var propertyName = String(property);
if (propertyName.endsWith('/'))
return propertyName in collectionCache;
return property in target;
},
});
}
function createCache_NoCache(cacheConfig) {
return new Proxy({
/*CACHE*/
}, {
set: function (target, property, value, receiver) {
return true;
},
get: function (target, property, receiver) {
return undefined;
},
deleteProperty: function (target, property) {
return true;
},
has: function (target, property) {
return false;
},
});
}
function createCache_LimitedByKeyCount(cacheConfig) {
var collectionCache = {};
var keyNumber = Math.ceil(cacheConfig), keys = Array(keyNumber), nextKeyIndex = 0, keyIndex;
return new Proxy({
/*CACHE*/
}, {
set: function (target, property, value, receiver) {
var propertyName = String(property);
if (propertyName.endsWith('/')) {
collectionCache[propertyName] = value;
return true;
}
updateKeys(target, propertyName, 'SET');
target[propertyName] = value;
Object.keys(collectionCache)
.filter(function (collection) { return keyInCollection(propertyName, collection); })
.forEach(function (collection) {
return collectionCache[collection].includes(propertyName) || collectionCache[collection].push(propertyName);
});
return true;
},
get: function (target, property, receiver) {
var propertyName = String(property);
if (propertyName.endsWith('/'))
return collectionCache[propertyName];
updateKeys(target, propertyName, 'GET');
return target[propertyName];
},
deleteProperty: function (target, property) {
var propertyName = String(property);
if (propertyName === '*') {
collectionCache = {};
keys = Array(keyNumber);
nextKeyIndex = 0;
return true;
}
if (propertyName.endsWith('/'))
return delete collectionCache[propertyName];
Object.keys(collectionCache)
.filter(function (collection) { return keyInCollection(propertyName, collection); })
.forEach(function (collection) {
return collectionCache[collection].includes(propertyName) &&
collectionCache[collection].splice(collectionCache[collection].indexOf(propertyName), 1);
});
updateKeys(target, propertyName, 'DELETE');
return delete target[propertyName];
},
has: function (target, property) {
var propertyName = String(property);
if (propertyName.endsWith('/'))
return propertyName in collectionCache;
return keys.indexOf(property) >= 0;
},
});
function realIndex(i) {
return (i + keyNumber) % keyNumber;
}
function updateKeys(target, property, mode) {
keyIndex = keys.indexOf(property);
if (keyIndex < 0) {
// Does not exist
mode === 'SET' && addKey();
}
else if (keyIndex === realIndex(nextKeyIndex - 1)) {
// The latest key
mode === 'DELETE' && removeKey();
}
else {
// Otherwise
removeKey();
mode === 'DELETE' || addKey();
}
function removeKey() {
while (keyIndex !== nextKeyIndex && keys[keyIndex]) {
keys[keyIndex] = keys[realIndex(keyIndex - 1)];
keyIndex = realIndex(keyIndex - 1);
}
keys[nextKeyIndex] = undefined;
}
function addKey() {
if (keys[nextKeyIndex] !== property) {
if (keys[nextKeyIndex] !== undefined)
delete target[keys[nextKeyIndex]];
keys[nextKeyIndex] = property;
}
nextKeyIndex = realIndex(nextKeyIndex + 1);
}
}
}
function keyInCollection(key, collection) {
collection = collection.startsWith('./')
? collection.slice(1)
: collection.startsWith('/')
? collection
: '/' + collection;
key = key.startsWith('./') ? key.slice(1) : key.startsWith('/') ? key : '/' + key;
return key.startsWith(collection);
}
}
exports.default = createCache;