Comparing version 2.0.0 to 3.0.0
@@ -7,2 +7,13 @@ # Change Log | ||
## [3.0.0] - 2017-08-19 | ||
- Library loading was changed: | ||
- **Now you need to instantiate the `new InCache` object** | ||
- Added cache writing to disk | ||
- Added `setConfig` method | ||
- Added `addTo` method | ||
- Added `prependTo` method | ||
- Added `updateIn` method | ||
- Added `removeFrom` method | ||
- Added global record configuration | ||
## [2.0.0] - 2017-08-13 | ||
@@ -9,0 +20,0 @@ - Changed `set` method: last argument now is an object, see documentation. |
@@ -1,2 +0,2 @@ | ||
// [AIV] InCache Build version: 2.0.0 | ||
// [AIV] InCache Build version: 3.0.0 | ||
var incache = | ||
@@ -65,3 +65,3 @@ /******/ (function(modules) { // webpackBootstrap | ||
/******/ // Load entry module and return exports | ||
/******/ return __webpack_require__(__webpack_require__.s = 0); | ||
/******/ return __webpack_require__(__webpack_require__.s = 1); | ||
/******/ }) | ||
@@ -76,299 +76,2 @@ /************************************************************************/ | ||
module.exports = __webpack_require__(1); | ||
/***/ }), | ||
/* 1 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
"use strict"; | ||
/* WEBPACK VAR INJECTION */(function(process, global) { | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
var helper = __webpack_require__(4); | ||
/** | ||
* @namespace incache | ||
*/ | ||
var incache = {}; | ||
/** | ||
* Global key | ||
* @type {string} | ||
* @ignore | ||
*/ | ||
var GLOBAL_KEY = '___incache___storage___global___key___'; | ||
/** | ||
* Default options | ||
* @type {{silent: boolean, life: number}} | ||
* @ignore | ||
*/ | ||
var DEFAULT_OPTS = { | ||
silent: false, | ||
life: 0 | ||
}; | ||
/** | ||
* Root object | ||
* @ignore | ||
*/ | ||
var root = (typeof process === 'undefined' ? 'undefined' : _typeof(process)) === 'object' && typeof process.pid !== 'undefined' ? global : window; | ||
if (!root[GLOBAL_KEY]) root[GLOBAL_KEY] = {}; | ||
/** | ||
* Short storage | ||
* @ignore | ||
*/ | ||
var storage = root[GLOBAL_KEY]; | ||
var _onRemoved = function _onRemoved() {}; | ||
var _onCreated = function _onCreated() {}; | ||
var _onUpdated = function _onUpdated() {}; | ||
/** | ||
* Set/update record | ||
* @param key {any} | ||
* @param value {any} | ||
* @param [opts] {Object} options object | ||
* @param [opts.silent=false] {boolean} if true no event will be triggered | ||
* @param [opts.life=0] {number} seconds of life. If 0 not expire. | ||
* @returns {{isNew: boolean, createdOn: Date|null, updatedOn: Date|null, value: *}} | ||
* @example | ||
* incache.set('my key', 'my value'); | ||
* incache.set('my object', {a: 1, b: 2}); | ||
* incache.set('my boolean', true, {life: 2}); // Expires after 2 seconds | ||
*/ | ||
incache.set = function (key, value) { | ||
var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
var record = { | ||
isNew: true, | ||
createdOn: null, | ||
updatedOn: null, | ||
expiresOn: null, | ||
value: value | ||
}; | ||
opts = helper.defaults(opts, DEFAULT_OPTS); | ||
if (opts.life && helper.is(opts.life, 'number')) { | ||
record.expiresOn = helper.addSecondsToNow(opts.life); | ||
} | ||
if (incache.has(key)) { | ||
record.isNew = false; | ||
record.updatedOn = new Date(); | ||
if (!opts.silent) _onUpdated.call(undefined, key, record); | ||
} else { | ||
record.createdOn = new Date(); | ||
if (!opts.silent) _onCreated.call(undefined, key, record); | ||
} | ||
storage[key] = record; | ||
return record; | ||
}; | ||
/** | ||
* Set/update multiple records. This method not trigger any event. | ||
* @param records {array} array of object, e.g. [{key: foo1, value: bar1},{key: foo2, value: bar2}] | ||
* @example | ||
* incache.bulkSet([ | ||
* {key: 'my key 1', value: 'my value 1'}, | ||
* {key: 'my key 2', value: 'my value 2'}, | ||
* {key: 'my key 3', value: 'my value 3'}, | ||
* {key: 'my key 4', value: 'my value 4'} | ||
* ]); | ||
*/ | ||
incache.bulkSet = function (records) { | ||
if (!helper.is(records, 'array')) throw new Error('records must be an array of object, e.g. {key: foo, value: bar}'); | ||
for (var i = 0; i < records.length; i++) { | ||
if (helper.is(records[i].key, 'undefined') || helper.is(records[i].value, 'undefined')) throw new Error('key and value properties are required'); | ||
incache.set(records[i].key, records[i].value, { silent: true }); | ||
} | ||
}; | ||
/** | ||
* Get record by key | ||
* @param key {any} | ||
* @param [onlyValue=true] {boolean} if false get incache record | ||
* @returns {any|null} | ||
* @example | ||
* incache.get('my key'); | ||
*/ | ||
incache.get = function (key) { | ||
var onlyValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; | ||
if (incache.has(key)) { | ||
if (incache.expired(key)) { | ||
incache.remove(key, true); | ||
return null; | ||
} | ||
return onlyValue ? storage[key].value : storage[key]; | ||
} else { | ||
return null; | ||
} | ||
}; | ||
/** | ||
* Delete a record | ||
* @param key {any} | ||
* @param [silent=false] {boolean} if true no event will be triggered | ||
* @example | ||
* incache.remove('my key'); | ||
*/ | ||
incache.remove = function (key) { | ||
var silent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; | ||
delete storage[key]; | ||
if (!silent) _onRemoved.call(undefined, key); | ||
}; | ||
/** | ||
* Delete multiple records | ||
* @param keys {array} an array of keys | ||
* @example | ||
* incache.bulkRemove(['key1', 'key2', 'key3']); | ||
*/ | ||
incache.bulkRemove = function (keys) { | ||
if (!helper.is(keys, 'array')) throw new Error('keys must be an array of keys'); | ||
for (var i = 0; i < keys.length; i++) { | ||
incache.remove(keys[i], true); | ||
} | ||
}; | ||
/** | ||
* Fetch all records | ||
* @returns {Array} | ||
*/ | ||
incache.all = function () { | ||
var records = []; | ||
for (var key in storage) { | ||
if (storage.hasOwnProperty(key)) { | ||
if (incache.expired(key)) { | ||
incache.remove(key, true); | ||
} else { | ||
records.push({ | ||
key: key, | ||
value: storage[key].value | ||
}); | ||
} | ||
} | ||
} | ||
return records; | ||
}; | ||
/** | ||
* Check if record is expired | ||
* @param key {any} | ||
* @returns {boolean} | ||
*/ | ||
incache.expired = function (key) { | ||
if (storage[key] && storage[key].expiresOn) { | ||
var now = new Date(); | ||
var expiry = new Date(storage[key].expiresOn); | ||
return now > expiry; | ||
} else { | ||
return false; | ||
} | ||
}; | ||
/** | ||
* Remove all records | ||
*/ | ||
incache.clear = function () { | ||
/** | ||
* Reset object | ||
* @ignore | ||
*/ | ||
storage = root[GLOBAL_KEY] = {}; | ||
}; | ||
/** | ||
* Check if key exists | ||
* @param key {any} | ||
* @returns {boolean} | ||
* @example | ||
* incache.has('my key'); | ||
*/ | ||
incache.has = function (key) { | ||
return storage.hasOwnProperty(key); | ||
}; | ||
/** | ||
* Triggered when a record has been deleted | ||
* @param callback {incache.onRemoved~removedCallback} callback function | ||
* @example | ||
* incache.onRemoved((key)=>{ | ||
* console.log('removed', key); | ||
* }); | ||
*/ | ||
incache.onRemoved = function (callback) { | ||
_onRemoved = callback; | ||
}; | ||
/** | ||
* onRemoved callback | ||
* @callback incache.onRemoved~removedCallback | ||
* @param key {string} key of record removed | ||
*/ | ||
/** | ||
* Triggered when a record has been created | ||
* @param callback {incache.onCreated~createdCallback} callback function | ||
* @example | ||
* incache.onCreated((key, record)=>{ | ||
* console.log('created', key, record); | ||
* }); | ||
*/ | ||
incache.onCreated = function (callback) { | ||
_onCreated = callback; | ||
}; | ||
/** | ||
* onCreated callback | ||
* @callback incache.onCreated~createdCallback | ||
* @param key {string} key of record created | ||
* @param record {Object} record object | ||
*/ | ||
/** | ||
* Triggered when a record has been updated | ||
* @param callback {incache.onUpdated~updatedCallback} callback function | ||
* @example | ||
* incache.onUpdated((key, record)=>{ | ||
* console.log('updated', key, record); | ||
* }); | ||
*/ | ||
incache.onUpdated = function (callback) { | ||
_onUpdated = callback; | ||
}; | ||
/** | ||
* onUpdated callback | ||
* @callback incache.onUpdated~updatedCallback | ||
* @param key {string} key of record updated | ||
* @param record {Object} record object | ||
*/ | ||
/** | ||
* Expose module | ||
*/ | ||
module.exports = incache; | ||
module.exports._global_key = GLOBAL_KEY; | ||
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2), __webpack_require__(3))) | ||
/***/ }), | ||
/* 2 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
"use strict"; | ||
// shim for using process in browser | ||
@@ -561,2 +264,575 @@ var process = module.exports = {}; | ||
/***/ }), | ||
/* 1 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
"use strict"; | ||
module.exports = __webpack_require__(2); | ||
/***/ }), | ||
/* 2 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
"use strict"; | ||
/* WEBPACK VAR INJECTION */(function(global, process) { | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
var helper = __webpack_require__(4); | ||
var fs = __webpack_require__(5); | ||
var InCache = function () { | ||
/** | ||
* Create instance | ||
* @param [opts] {Object} configuration object | ||
* @param [opts.save=true] {boolean} if true saves cache in disk. (server only) | ||
* @param [opts.filePath=.incache] {string} cache file path | ||
* @param [opts.storeName] {string} store name | ||
* @param [opts.global] {Object} global record configuration | ||
* @param [opts.global.silent=false] {boolean} if true no event will be triggered | ||
* @param [opts.global.life=0] {number} max age. If 0 not expire | ||
* @constructor | ||
*/ | ||
function InCache() { | ||
var _this = this; | ||
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
_classCallCheck(this, InCache); | ||
/** | ||
* Global key | ||
* @type {string} | ||
* @ignore | ||
*/ | ||
this.GLOBAL_KEY = '___InCache___storage___global___key___'; | ||
/** | ||
* Root object | ||
* @ignore | ||
*/ | ||
this._root = helper.isServer() ? global : window; | ||
/** | ||
* InCache default configuration | ||
* @type {{storeName: string, save: boolean, filePath: string, global: {silent: boolean, life: number}}} | ||
* @ignore | ||
*/ | ||
this.DEFAULT_CONFIG = { | ||
storeName: '', | ||
save: true, | ||
filePath: '.incache', | ||
global: { | ||
silent: false, | ||
life: 0 | ||
} | ||
}; | ||
// Defines callback private | ||
this._onRemoved = function () {}; | ||
this._onCreated = function () {}; | ||
this._onUpdated = function () {}; | ||
if (helper.isServer()) { | ||
process.stdin.resume(); | ||
process.on('exit', function () { | ||
_this._write(); | ||
}); | ||
process.on('SIGINT', function () { | ||
_this._write(); | ||
}); | ||
} | ||
this.setConfig(opts); | ||
} | ||
_createClass(InCache, [{ | ||
key: '_write', | ||
value: function _write() { | ||
var _memory = this._memory, | ||
config = _memory.config, | ||
data = _memory.data; | ||
if (config.save) { | ||
var content = JSON.stringify(data); | ||
fs.writeFileSync(config.filePath, content); | ||
} | ||
} | ||
}, { | ||
key: '_read', | ||
value: function _read() { | ||
var config = this._memory.config; | ||
if (config.save && fs.existsSync(config.filePath)) { | ||
var content = fs.readFileSync(config.filePath); | ||
try { | ||
this._storage = this._memory.data = JSON.parse(content); | ||
} catch (e) { | ||
this._storage = this._memory.data = {}; | ||
} | ||
} | ||
} | ||
/** | ||
* Set configuration | ||
* @param [opts] {Object} configuration object | ||
* @param [opts.save=true] {boolean} if true saves cache in disk. (server only) | ||
* @param [opts.filePath=.incache] {string} cache file path | ||
* @param [opts.storeName] {string} store name | ||
* @param [opts.global] {Object} global record configuration | ||
* @param [opts.global.silent=false] {boolean} if true no event will be triggered | ||
* @param [opts.global.life=0] {number} max age. If 0 not expire | ||
*/ | ||
}, { | ||
key: 'setConfig', | ||
value: function setConfig() { | ||
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
if (opts.storeName) this.GLOBAL_KEY += opts.storeName; | ||
if (!this._root[this.GLOBAL_KEY]) { | ||
this._root[this.GLOBAL_KEY] = { | ||
metadata: { | ||
lastSave: null | ||
}, | ||
data: {}, | ||
config: this.DEFAULT_CONFIG | ||
}; | ||
} | ||
this._root[this.GLOBAL_KEY].config = helper.defaults(opts, this.DEFAULT_CONFIG); | ||
this._memory = this._root[this.GLOBAL_KEY]; | ||
this._storage = this._memory.data; | ||
if (helper.isServer()) this._read(); | ||
} | ||
/** | ||
* Get configuration | ||
* @returns {*} | ||
*/ | ||
}, { | ||
key: 'getConfig', | ||
value: function getConfig() { | ||
return this._memory.config; | ||
} | ||
/** | ||
* Set/update record | ||
* @param key {any} | ||
* @param value {any} | ||
* @param [opts] {Object} options object | ||
* @param [opts.silent=false] {boolean} if true no event will be triggered. (overwrites global configuration) | ||
* @param [opts.life=0] {number} max age. If 0 not expire. (overwrites global configuration) | ||
* @returns {{isNew: boolean, createdOn: Date|null, updatedOn: Date|null, value: *}} | ||
* @example | ||
* inCache.set('my key', 'my value'); | ||
* inCache.set('my object', {a: 1, b: 2}); | ||
* inCache.set('my boolean', true, {life: 2}); // Expires after 2 seconds | ||
*/ | ||
}, { | ||
key: 'set', | ||
value: function set(key, value) { | ||
var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
var record = { | ||
isNew: true, | ||
createdOn: null, | ||
updatedOn: null, | ||
expiresOn: null, | ||
value: value | ||
}; | ||
opts = helper.defaults(opts, this.DEFAULT_CONFIG.global); | ||
if (opts.life && helper.is(opts.life, 'number')) { | ||
record.expiresOn = helper.addSecondsToNow(opts.life); | ||
} | ||
if (this.has(key)) { | ||
record.isNew = false; | ||
record.updatedOn = new Date(); | ||
if (!opts.silent) this._onUpdated.call(this, key, record); | ||
} else { | ||
record.createdOn = new Date(); | ||
if (!opts.silent) this._onCreated.call(this, key, record); | ||
} | ||
this._storage[key] = record; | ||
return record; | ||
} | ||
/** | ||
* Set/update multiple records. This method not trigger any event. | ||
* @param records {array} array of object, e.g. [{key: foo1, value: bar1},{key: foo2, value: bar2}] | ||
* @example | ||
* inCache.bulkSet([ | ||
* {key: 'my key 1', value: 'my value 1'}, | ||
* {key: 'my key 2', value: 'my value 2'}, | ||
* {key: 'my key 3', value: 'my value 3'}, | ||
* {key: 'my key 4', value: 'my value 4'} | ||
* ]); | ||
*/ | ||
}, { | ||
key: 'bulkSet', | ||
value: function bulkSet(records) { | ||
if (!helper.is(records, 'array')) throw new Error('records must be an array of object, e.g. {key: foo, value: bar}'); | ||
for (var i = 0; i < records.length; i++) { | ||
if (helper.is(records[i].key, 'undefined') || helper.is(records[i].value, 'undefined')) throw new Error('key and value properties are required'); | ||
this.set(records[i].key, records[i].value, { silent: true, fromBulk: true }); | ||
} | ||
} | ||
/** | ||
* Get record by key | ||
* @param key {any} | ||
* @param [onlyValue=true] {boolean} if false get InCache record | ||
* @returns {any|null} | ||
* @example | ||
* inCache.get('my key'); | ||
*/ | ||
}, { | ||
key: 'get', | ||
value: function get(key) { | ||
var onlyValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; | ||
if (this.has(key)) { | ||
if (this.expired(key)) { | ||
this.remove(key, true); | ||
return null; | ||
} | ||
return onlyValue ? this._storage[key].value : this._storage[key]; | ||
} else { | ||
return null; | ||
} | ||
} | ||
/** | ||
* Delete a record | ||
* @param key {any} | ||
* @param [silent=false] {boolean} if true no event will be triggered | ||
* @param [opts] {Object} optional arguments | ||
* @example | ||
* inCache.remove('my key'); | ||
*/ | ||
}, { | ||
key: 'remove', | ||
value: function remove(key) { | ||
var silent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; | ||
var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
delete this._storage[key]; | ||
if (!silent) this._onRemoved.call(this, key); | ||
} | ||
/** | ||
* Given a key that has value like an array adds value to end of array | ||
* @param key {any} | ||
* @param value {any} | ||
* @returns {*} | ||
* @example | ||
* inCache.set('myArray', ['hello', 'world']); | ||
* inCache.addTo('myArray', 'ciao'); //-> ['hello', 'world', 'ciao']; | ||
*/ | ||
}, { | ||
key: 'addTo', | ||
value: function addTo(key, value) { | ||
if (!this.has(key)) return null; | ||
var record = this.get(key); | ||
if (!helper.is(record, 'array')) throw new Error('object must be an array'); | ||
record.push(value); | ||
return this.set(key, record); | ||
} | ||
/** | ||
* Given a key that has value like an array adds value to beginning of array | ||
* @param key {any} | ||
* @param value {any} | ||
* @returns {*} | ||
* @example | ||
* inCache.set('myArray', ['hello', 'world']); | ||
* inCache.prependTo('myArray', 'ciao'); //-> ['ciao', 'hello', 'world']; | ||
*/ | ||
}, { | ||
key: 'prependTo', | ||
value: function prependTo(key, value) { | ||
if (!this.has(key)) return null; | ||
var record = this.get(key); | ||
if (!helper.is(record, 'array')) throw new Error('object must be an array'); | ||
record.unshift(value); | ||
return this.set(key, record); | ||
} | ||
/** | ||
* Given a key that has value like an array updates key(s) if `where` is satisfied | ||
* @param key {any} | ||
* @param value {any} | ||
* @param where {any} | ||
* @example | ||
* inCache.set('myArray', ['hello', 'world']); | ||
* inCache.updateIn('myArray', 'ciao', 'hello'); //-> ['ciao', 'world']; | ||
* | ||
* inCache.set('myArray', [{a: 1, b: 2, c: 3], {b: 2, c: 3}, {b: 4, e: 5}); | ||
* inCache.updateIn('myArray', {z: 0, x: 0}, {b: 2, c: 3}); //-> [{z: 0, x: 0}, {z: 0, x: 0}, {b: 4, e: 5}]; | ||
*/ | ||
}, { | ||
key: 'updateIn', | ||
value: function updateIn(key, value, where) { | ||
if (!this.has(key)) return null; | ||
if (helper.is(value, 'undefined')) throw new Error('value cannot be undefined'); | ||
if (helper.is(where, 'undefined')) throw new Error('where cannot be undefined'); | ||
var recordValue = this.get(key); | ||
if (!helper.is(recordValue, 'array')) throw new Error('value must be an array'); | ||
var updated = false; | ||
for (var i in recordValue) { | ||
if (recordValue.hasOwnProperty(i)) { | ||
var result = []; | ||
for (var prop in where) { | ||
if (where.hasOwnProperty(prop)) if (helper.is(where, 'object')) result.push(typeof recordValue[i][prop] !== 'undefined' && recordValue[i][prop] === where[prop]);else result.push(recordValue[i] === where); | ||
} | ||
if (result.length && result.indexOf(false) === -1) { | ||
updated = true; | ||
recordValue[i] = value; | ||
} | ||
} | ||
} | ||
if (updated) { | ||
this.set(key, recordValue); | ||
} | ||
} | ||
/** | ||
* Given a key that has value like an array removes key(s) if `where` is satisfied | ||
* @param key {any} | ||
* @param where {any} | ||
* @example | ||
* inCache.set('myArray', ['hello', 'world']); | ||
* inCache.removeFrom('myArray', 'hello'); //-> ['world']; | ||
*/ | ||
}, { | ||
key: 'removeFrom', | ||
value: function removeFrom(key, where) { | ||
if (!this.has(key)) return null; | ||
if (helper.is(where, 'undefined')) throw new Error('where cannot be undefined'); | ||
var recordValue = this.get(key); | ||
if (!helper.is(recordValue, 'array')) throw new Error('value must be an array'); | ||
var recordLengthBefore = recordValue.length; | ||
for (var i in recordValue) { | ||
if (recordValue.hasOwnProperty(i)) { | ||
var result = []; | ||
for (var prop in where) { | ||
if (where.hasOwnProperty(prop)) if (helper.is(where, 'object')) result.push(typeof recordValue[i][prop] !== 'undefined' && recordValue[i][prop] === where[prop]);else result.push(recordValue[i] === where); | ||
} | ||
if (result.length && result.indexOf(false) === -1) recordValue.splice(i, 1); | ||
} | ||
} | ||
if (recordLengthBefore !== recordValue.length) { | ||
this.set(key, recordValue); | ||
} | ||
} | ||
/** | ||
* Delete multiple records | ||
* @param keys {array} an array of keys | ||
* @example | ||
* inCache.bulkRemove(['key1', 'key2', 'key3']); | ||
*/ | ||
}, { | ||
key: 'bulkRemove', | ||
value: function bulkRemove(keys) { | ||
if (!helper.is(keys, 'array')) throw new Error('keys must be an array of keys'); | ||
for (var i = 0; i < keys.length; i++) { | ||
this.remove(keys[i], true, { fromBulk: true }); | ||
} | ||
} | ||
/** | ||
* Fetch all records | ||
* @returns {Array} | ||
*/ | ||
}, { | ||
key: 'all', | ||
value: function all() { | ||
var records = []; | ||
for (var key in this._storage) { | ||
if (this._storage.hasOwnProperty(key)) { | ||
if (this.expired(key)) { | ||
this.remove(key, true); | ||
} else { | ||
records.push({ | ||
key: key, | ||
value: this._storage[key].value | ||
}); | ||
} | ||
} | ||
} | ||
return records; | ||
} | ||
/** | ||
* Check if record is expired | ||
* @param key {any} | ||
* @returns {boolean} | ||
*/ | ||
}, { | ||
key: 'expired', | ||
value: function expired(key) { | ||
if (this._storage[key] && this._storage[key].expiresOn) { | ||
var now = new Date(); | ||
var expiry = new Date(this._storage[key].expiresOn); | ||
return now > expiry; | ||
} else { | ||
return false; | ||
} | ||
} | ||
/** | ||
* Remove all records | ||
*/ | ||
}, { | ||
key: 'clear', | ||
value: function clear() { | ||
/** | ||
* Reset object | ||
* @ignore | ||
*/ | ||
this._storage = this._memory.data = {}; | ||
} | ||
/** | ||
* Check if key exists | ||
* @param key {any} | ||
* @returns {boolean} | ||
* @example | ||
* inCache.has('my key'); | ||
*/ | ||
}, { | ||
key: 'has', | ||
value: function has(key) { | ||
return this._storage.hasOwnProperty(key); | ||
} | ||
/** | ||
* Triggered when a record has been deleted | ||
* @param callback {InCache~removedCallback} callback function | ||
* @example | ||
* inCache.onRemoved((key)=>{ | ||
* console.log('removed', key); | ||
* }); | ||
*/ | ||
}, { | ||
key: 'onRemoved', | ||
value: function onRemoved(callback) { | ||
this._onRemoved = callback; | ||
} | ||
/** | ||
* onRemoved callback | ||
* @callback InCache~removedCallback | ||
* @param key {string} key of record removed | ||
*/ | ||
/** | ||
* Triggered when a record has been created | ||
* @param callback {InCache~createdCallback} callback function | ||
* @example | ||
* inCache.onCreated((key, record)=>{ | ||
* console.log('created', key, record); | ||
* }); | ||
*/ | ||
}, { | ||
key: 'onCreated', | ||
value: function onCreated(callback) { | ||
this._onCreated = callback; | ||
} | ||
/** | ||
* onCreated callback | ||
* @callback InCache~createdCallback | ||
* @param key {string} key of record created | ||
* @param record {Object} record object | ||
*/ | ||
/** | ||
* Triggered when a record has been updated | ||
* @param callback {InCache~updatedCallback} callback function | ||
* @example | ||
* inCache.onUpdated((key, record)=>{ | ||
* console.log('updated', key, record); | ||
* }); | ||
*/ | ||
}, { | ||
key: 'onUpdated', | ||
value: function onUpdated(callback) { | ||
this._onUpdated = callback; | ||
} | ||
/** | ||
* onUpdated callback | ||
* @callback InCache~updatedCallback | ||
* @param key {string} key of record updated | ||
* @param record {Object} record object | ||
*/ | ||
}]); | ||
return InCache; | ||
}(); | ||
/** | ||
* Expose module | ||
*/ | ||
module.exports = InCache; | ||
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3), __webpack_require__(0))) | ||
/***/ }), | ||
/* 3 */ | ||
@@ -596,4 +872,4 @@ /***/ (function(module, exports, __webpack_require__) { | ||
"use strict"; | ||
/* WEBPACK VAR INJECTION */(function(process) { | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
@@ -643,5 +919,21 @@ | ||
/** | ||
* Check if is Node environment | ||
* @returns {boolean} | ||
*/ | ||
helper.isServer = function () { | ||
return (typeof process === 'undefined' ? 'undefined' : _typeof(process)) === 'object' && typeof process.pid !== 'undefined'; | ||
}; | ||
module.exports = helper; | ||
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(0))) | ||
/***/ }), | ||
/* 5 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
"use strict"; | ||
/***/ }) | ||
/******/ ]); |
@@ -1,2 +0,2 @@ | ||
// [AIV] InCache Build version: 2.0.0 | ||
var incache=function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=0)}([function(e,t,n){"use strict";e.exports=n(1)},function(e,t,n){"use strict";(function(t,r){var o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},i=n(4),u={},c="___incache___storage___global___key___",a={silent:!1,life:0},l="object"===(void 0===t?"undefined":o(t))&&void 0!==t.pid?r:window;l[c]||(l[c]={});var f=l[c],s=function(){},p=function(){},y=function(){};u.set=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r={isNew:!0,createdOn:null,updatedOn:null,expiresOn:null,value:t};return n=i.defaults(n,a),n.life&&i.is(n.life,"number")&&(r.expiresOn=i.addSecondsToNow(n.life)),u.has(e)?(r.isNew=!1,r.updatedOn=new Date,n.silent||y.call(void 0,e,r)):(r.createdOn=new Date,n.silent||p.call(void 0,e,r)),f[e]=r,r},u.bulkSet=function(e){if(!i.is(e,"array"))throw new Error("records must be an array of object, e.g. {key: foo, value: bar}");for(var t=0;t<e.length;t++){if(i.is(e[t].key,"undefined")||i.is(e[t].value,"undefined"))throw new Error("key and value properties are required");u.set(e[t].key,e[t].value,{silent:!0})}},u.get=function(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];return u.has(e)?u.expired(e)?(u.remove(e,!0),null):t?f[e].value:f[e]:null},u.remove=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];delete f[e],t||s.call(void 0,e)},u.bulkRemove=function(e){if(!i.is(e,"array"))throw new Error("keys must be an array of keys");for(var t=0;t<e.length;t++)u.remove(e[t],!0)},u.all=function(){var e=[];for(var t in f)f.hasOwnProperty(t)&&(u.expired(t)?u.remove(t,!0):e.push({key:t,value:f[t].value}));return e},u.expired=function(e){if(f[e]&&f[e].expiresOn){return new Date>new Date(f[e].expiresOn)}return!1},u.clear=function(){f=l[c]={}},u.has=function(e){return f.hasOwnProperty(e)},u.onRemoved=function(e){s=e},u.onCreated=function(e){p=e},u.onUpdated=function(e){y=e},e.exports=u,e.exports._global_key=c}).call(t,n(2),n(3))},function(e,t,n){"use strict";function r(){throw new Error("setTimeout has not been defined")}function o(){throw new Error("clearTimeout has not been defined")}function i(e){if(s===setTimeout)return setTimeout(e,0);if((s===r||!s)&&setTimeout)return s=setTimeout,setTimeout(e,0);try{return s(e,0)}catch(t){try{return s.call(null,e,0)}catch(t){return s.call(this,e,0)}}}function u(e){if(p===clearTimeout)return clearTimeout(e);if((p===o||!p)&&clearTimeout)return p=clearTimeout,clearTimeout(e);try{return p(e)}catch(t){try{return p.call(null,e)}catch(t){return p.call(this,e)}}}function c(){m&&d&&(m=!1,d.length?h=d.concat(h):v=-1,h.length&&a())}function a(){if(!m){var e=i(c);m=!0;for(var t=h.length;t;){for(d=h,h=[];++v<t;)d&&d[v].run();v=-1,t=h.length}d=null,m=!1,u(e)}}function l(e,t){this.fun=e,this.array=t}function f(){}var s,p,y=e.exports={};!function(){try{s="function"==typeof setTimeout?setTimeout:r}catch(e){s=r}try{p="function"==typeof clearTimeout?clearTimeout:o}catch(e){p=o}}();var d,h=[],m=!1,v=-1;y.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];h.push(new l(e,t)),1!==h.length||m||i(a)},l.prototype.run=function(){this.fun.apply(null,this.array)},y.title="browser",y.browser=!0,y.env={},y.argv=[],y.version="",y.versions={},y.on=f,y.addListener=f,y.once=f,y.off=f,y.removeListener=f,y.removeAllListeners=f,y.emit=f,y.prependListener=f,y.prependOnceListener=f,y.listeners=function(e){return[]},y.binding=function(e){throw new Error("process.binding is not supported")},y.cwd=function(){return"/"},y.chdir=function(e){throw new Error("process.chdir is not supported")},y.umask=function(){return 0}},function(e,t,n){"use strict";var r,o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};r=function(){return this}();try{r=r||Function("return this")()||(0,eval)("this")}catch(e){"object"===("undefined"==typeof window?"undefined":o(window))&&(r=window)}e.exports=r},function(e,t,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o={};o.is=function(e,t){return Object.prototype.toString.call(e).toLowerCase()==="[object "+t+"]".toLowerCase()},o.defaults=function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e.hasOwnProperty(n)?"object"===r(e[n])&&o.defaults(e[n],t[n]):e[n]=t[n]);return e},o.addSecondsToNow=function(e){var t=new Date;return new Date(t.setSeconds(t.getSeconds()+e))},e.exports=o}]); | ||
// [AIV] InCache Build version: 3.0.0 | ||
var incache=function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};return t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=1)}([function(e,t,r){"use strict";function n(){throw new Error("setTimeout has not been defined")}function o(){throw new Error("clearTimeout has not been defined")}function i(e){if(c===setTimeout)return setTimeout(e,0);if((c===n||!c)&&setTimeout)return c=setTimeout,setTimeout(e,0);try{return c(e,0)}catch(t){try{return c.call(null,e,0)}catch(t){return c.call(this,e,0)}}}function a(e){if(h===clearTimeout)return clearTimeout(e);if((h===o||!h)&&clearTimeout)return h=clearTimeout,clearTimeout(e);try{return h(e)}catch(t){try{return h.call(null,e)}catch(t){return h.call(this,e)}}}function s(){p&&y&&(p=!1,y.length?v=y.concat(v):m=-1,v.length&&u())}function u(){if(!p){var e=i(s);p=!0;for(var t=v.length;t;){for(y=v,v=[];++m<t;)y&&y[m].run();m=-1,t=v.length}y=null,p=!1,a(e)}}function f(e,t){this.fun=e,this.array=t}function l(){}var c,h,d=e.exports={};!function(){try{c="function"==typeof setTimeout?setTimeout:n}catch(e){c=n}try{h="function"==typeof clearTimeout?clearTimeout:o}catch(e){h=o}}();var y,v=[],p=!1,m=-1;d.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var r=1;r<arguments.length;r++)t[r-1]=arguments[r];v.push(new f(e,t)),1!==v.length||p||i(u)},f.prototype.run=function(){this.fun.apply(null,this.array)},d.title="browser",d.browser=!0,d.env={},d.argv=[],d.version="",d.versions={},d.on=l,d.addListener=l,d.once=l,d.off=l,d.removeListener=l,d.removeAllListeners=l,d.emit=l,d.prependListener=l,d.prependOnceListener=l,d.listeners=function(e){return[]},d.binding=function(e){throw new Error("process.binding is not supported")},d.cwd=function(){return"/"},d.chdir=function(e){throw new Error("process.chdir is not supported")},d.umask=function(){return 0}},function(e,t,r){"use strict";e.exports=r(2)},function(e,t,r){"use strict";(function(t,n){function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var i=function(){function e(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,r,n){return r&&e(t.prototype,r),n&&e(t,n),t}}(),a=r(4),s=r(5),u=function(){function e(){var r=this,i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};o(this,e),this.GLOBAL_KEY="___InCache___storage___global___key___",this._root=a.isServer()?t:window,this.DEFAULT_CONFIG={storeName:"",save:!0,filePath:".incache",global:{silent:!1,life:0}},this._onRemoved=function(){},this._onCreated=function(){},this._onUpdated=function(){},a.isServer()&&(n.stdin.resume(),n.on("exit",function(){r._write()}),n.on("SIGINT",function(){r._write()})),this.setConfig(i)}return i(e,[{key:"_write",value:function(){var e=this._memory,t=e.config,r=e.data;if(t.save){var n=JSON.stringify(r);s.writeFileSync(t.filePath,n)}}},{key:"_read",value:function(){var e=this._memory.config;if(e.save&&s.existsSync(e.filePath)){var t=s.readFileSync(e.filePath);try{this._storage=this._memory.data=JSON.parse(t)}catch(e){this._storage=this._memory.data={}}}}},{key:"setConfig",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};e.storeName&&(this.GLOBAL_KEY+=e.storeName),this._root[this.GLOBAL_KEY]||(this._root[this.GLOBAL_KEY]={metadata:{lastSave:null},data:{},config:this.DEFAULT_CONFIG}),this._root[this.GLOBAL_KEY].config=a.defaults(e,this.DEFAULT_CONFIG),this._memory=this._root[this.GLOBAL_KEY],this._storage=this._memory.data,a.isServer()&&this._read()}},{key:"getConfig",value:function(){return this._memory.config}},{key:"set",value:function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},n={isNew:!0,createdOn:null,updatedOn:null,expiresOn:null,value:t};return r=a.defaults(r,this.DEFAULT_CONFIG.global),r.life&&a.is(r.life,"number")&&(n.expiresOn=a.addSecondsToNow(r.life)),this.has(e)?(n.isNew=!1,n.updatedOn=new Date,r.silent||this._onUpdated.call(this,e,n)):(n.createdOn=new Date,r.silent||this._onCreated.call(this,e,n)),this._storage[e]=n,n}},{key:"bulkSet",value:function(e){if(!a.is(e,"array"))throw new Error("records must be an array of object, e.g. {key: foo, value: bar}");for(var t=0;t<e.length;t++){if(a.is(e[t].key,"undefined")||a.is(e[t].value,"undefined"))throw new Error("key and value properties are required");this.set(e[t].key,e[t].value,{silent:!0,fromBulk:!0})}}},{key:"get",value:function(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];return this.has(e)?this.expired(e)?(this.remove(e,!0),null):t?this._storage[e].value:this._storage[e]:null}},{key:"remove",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];arguments.length>2&&void 0!==arguments[2]&&arguments[2];delete this._storage[e],t||this._onRemoved.call(this,e)}},{key:"addTo",value:function(e,t){if(!this.has(e))return null;var r=this.get(e);if(!a.is(r,"array"))throw new Error("object must be an array");return r.push(t),this.set(e,r)}},{key:"prependTo",value:function(e,t){if(!this.has(e))return null;var r=this.get(e);if(!a.is(r,"array"))throw new Error("object must be an array");return r.unshift(t),this.set(e,r)}},{key:"updateIn",value:function(e,t,r){if(!this.has(e))return null;if(a.is(t,"undefined"))throw new Error("value cannot be undefined");if(a.is(r,"undefined"))throw new Error("where cannot be undefined");var n=this.get(e);if(!a.is(n,"array"))throw new Error("value must be an array");var o=!1;for(var i in n)if(n.hasOwnProperty(i)){var s=[];for(var u in r)r.hasOwnProperty(u)&&(a.is(r,"object")?s.push(void 0!==n[i][u]&&n[i][u]===r[u]):s.push(n[i]===r));s.length&&-1===s.indexOf(!1)&&(o=!0,n[i]=t)}o&&this.set(e,n)}},{key:"removeFrom",value:function(e,t){if(!this.has(e))return null;if(a.is(t,"undefined"))throw new Error("where cannot be undefined");var r=this.get(e);if(!a.is(r,"array"))throw new Error("value must be an array");var n=r.length;for(var o in r)if(r.hasOwnProperty(o)){var i=[];for(var s in t)t.hasOwnProperty(s)&&(a.is(t,"object")?i.push(void 0!==r[o][s]&&r[o][s]===t[s]):i.push(r[o]===t));i.length&&-1===i.indexOf(!1)&&r.splice(o,1)}n!==r.length&&this.set(e,r)}},{key:"bulkRemove",value:function(e){if(!a.is(e,"array"))throw new Error("keys must be an array of keys");for(var t=0;t<e.length;t++)this.remove(e[t],!0,{fromBulk:!0})}},{key:"all",value:function(){var e=[];for(var t in this._storage)this._storage.hasOwnProperty(t)&&(this.expired(t)?this.remove(t,!0):e.push({key:t,value:this._storage[t].value}));return e}},{key:"expired",value:function(e){if(this._storage[e]&&this._storage[e].expiresOn){return new Date>new Date(this._storage[e].expiresOn)}return!1}},{key:"clear",value:function(){this._storage=this._memory.data={}}},{key:"has",value:function(e){return this._storage.hasOwnProperty(e)}},{key:"onRemoved",value:function(e){this._onRemoved=e}},{key:"onCreated",value:function(e){this._onCreated=e}},{key:"onUpdated",value:function(e){this._onUpdated=e}}]),e}();e.exports=u}).call(t,r(3),r(0))},function(e,t,r){"use strict";var n,o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"===("undefined"==typeof window?"undefined":o(window))&&(n=window)}e.exports=n},function(e,t,r){"use strict";(function(t){var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n={};n.is=function(e,t){return Object.prototype.toString.call(e).toLowerCase()==="[object "+t+"]".toLowerCase()},n.defaults=function(e,t){for(var o in t)t.hasOwnProperty(o)&&(e.hasOwnProperty(o)?"object"===r(e[o])&&n.defaults(e[o],t[o]):e[o]=t[o]);return e},n.addSecondsToNow=function(e){var t=new Date;return new Date(t.setSeconds(t.getSeconds()+e))},n.isServer=function(){return"object"===(void 0===t?"undefined":r(t))&&void 0!==t.pid},e.exports=n}).call(t,r(0))},function(e,t,r){"use strict"}]); |
{ | ||
"name": "incache", | ||
"version": "2.0.0", | ||
"description": "Share singleton via global object, yes you can!", | ||
"version": "3.0.0", | ||
"description": "Simple key/value in-memory storage or on disk to persist some data", | ||
"main": "index.js", | ||
@@ -13,3 +13,6 @@ "scripts": { | ||
"test": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage", | ||
"doc": "jsdoc2md --no-gfm src/incache.js > api.md" | ||
"doc": "jsdoc2md --no-gfm src/incache.js > api.md", | ||
"benchmark:app": "node ./test/app/index.js", | ||
"benchmark:koa": "node ./node_modules/artillery/bin/artillery quick -d 10 -r 1000 -k http://127.0.0.1:3188/benchmark/notCached", | ||
"benchmark:cache": "node ./node_modules/artillery/bin/artillery quick -d 10 -r 1000 -k http://127.0.0.1:3188/benchmark/cached" | ||
}, | ||
@@ -25,2 +28,7 @@ "repository": { | ||
"storage", | ||
"session", | ||
"http", | ||
"response", | ||
"data", | ||
"caching", | ||
"share" | ||
@@ -35,3 +43,4 @@ ], | ||
"devDependencies": { | ||
"babel-core": "^6.25.0", | ||
"artillery": "^1.6.0-4", | ||
"babel-core": "^6.26.0", | ||
"babel-loader": "^7.1.1", | ||
@@ -43,8 +52,11 @@ "babel-preset-es2015": "^6.24.1", | ||
"jsdoc-to-markdown": "^3.0.0", | ||
"koa": "^2.3.0", | ||
"koa-router": "^7.2.1", | ||
"mocha": "^3.5.0", | ||
"mocha-lcov-reporter": "^1.3.0", | ||
"unminified-webpack-plugin": "^1.2.0", | ||
"webpack": "^3.5.3", | ||
"webpack": "^3.5.5", | ||
"webpack-auto-inject-version": "^0.5.14" | ||
} | ||
}, | ||
"dependencies": {} | ||
} |
@@ -5,3 +5,3 @@ <div align="center"> | ||
<br/><br/> | ||
Share singleton via global object, yes you can! | ||
Simple key/value in-memory storage or on disk to persist some data | ||
<br/><br/> | ||
@@ -14,2 +14,6 @@ <a href="https://travis-ci.org/fabioricali/incache" target="_blank"><img src="https://travis-ci.org/fabioricali/incache.svg?branch=master" title="Build Status"/></a> | ||
## Why? | ||
InCache is a module that store any info in memory, it can be used for example for storing **server sessions**, **caching http response** or **sharing singleton object** in your apps. | ||
It also give you the possibility to save data on disk so you can avoid the data loss when the process exit or restart. | ||
## Installation | ||
@@ -24,3 +28,4 @@ | ||
```javascript | ||
const store = require('incache'); | ||
const InCache = require('incache'); | ||
const store = new InCache(); | ||
@@ -62,4 +67,4 @@ // Create a record with key 'my key' | ||
### API Documentation | ||
See <a href="https://github.com/fabioricali/incache/blob/master/api.md">here</a> | ||
### API | ||
See <a href="https://github.com/fabioricali/incache/blob/master/api.md">full documentation</a> | ||
@@ -90,2 +95,5 @@ ### Browser | ||
## Author | ||
<a target="_blank" href="http://rica.li">Fabio Ricali</a> | ||
<a target="_blank" href="http://rica.li">Fabio Ricali</a> | ||
## Contributor | ||
<a target="_blank" href="https://www.mdslab.org">Davide Polano</a> |
@@ -44,2 +44,10 @@ const helper = {}; | ||
/** | ||
* Check if is Node environment | ||
* @returns {boolean} | ||
*/ | ||
helper.isServer = () => { | ||
return typeof process === 'object' && typeof process.pid !== 'undefined'; | ||
}; | ||
module.exports = helper; |
const helper = require('./helper'); | ||
const fs = require('fs'); | ||
/** | ||
* @namespace incache | ||
*/ | ||
const incache = {}; | ||
class InCache { | ||
/** | ||
* Create instance | ||
* @param [opts] {Object} configuration object | ||
* @param [opts.save=true] {boolean} if true saves cache in disk. (server only) | ||
* @param [opts.filePath=.incache] {string} cache file path | ||
* @param [opts.storeName] {string} store name | ||
* @param [opts.global] {Object} global record configuration | ||
* @param [opts.global.silent=false] {boolean} if true no event will be triggered | ||
* @param [opts.global.life=0] {number} max age. If 0 not expire | ||
* @constructor | ||
*/ | ||
constructor(opts = {}) { | ||
/** | ||
* Global key | ||
* @type {string} | ||
* @ignore | ||
*/ | ||
const GLOBAL_KEY = '___incache___storage___global___key___'; | ||
/** | ||
* Global key | ||
* @type {string} | ||
* @ignore | ||
*/ | ||
/** | ||
* Default options | ||
* @type {{silent: boolean, life: number}} | ||
* @ignore | ||
*/ | ||
const DEFAULT_OPTS = { | ||
silent: false, | ||
life: 0 | ||
}; | ||
this.GLOBAL_KEY = '___InCache___storage___global___key___'; | ||
/** | ||
* Root object | ||
* @ignore | ||
*/ | ||
const root = typeof process === 'object' && typeof process.pid !== 'undefined' ? global : window; | ||
/** | ||
* Root object | ||
* @ignore | ||
*/ | ||
this._root = helper.isServer() ? global : window; | ||
if (!root[GLOBAL_KEY]) | ||
root[GLOBAL_KEY] = {}; | ||
/** | ||
* InCache default configuration | ||
* @type {{storeName: string, save: boolean, filePath: string, global: {silent: boolean, life: number}}} | ||
* @ignore | ||
*/ | ||
this.DEFAULT_CONFIG = { | ||
storeName: '', | ||
save: true, | ||
filePath: '.incache', | ||
global: { | ||
silent: false, | ||
life: 0 | ||
} | ||
}; | ||
/** | ||
* Short storage | ||
* @ignore | ||
*/ | ||
let storage = root[GLOBAL_KEY]; | ||
// Defines callback private | ||
this._onRemoved = () => {}; | ||
this._onCreated = () => {}; | ||
this._onUpdated = () => {}; | ||
let _onRemoved = () => { | ||
}; | ||
let _onCreated = () => { | ||
}; | ||
let _onUpdated = () => { | ||
}; | ||
if(helper.isServer()) { | ||
process.stdin.resume(); | ||
process.on('exit', () => { | ||
this._write() | ||
}); | ||
process.on('SIGINT', () => { | ||
this._write() | ||
}); | ||
} | ||
/** | ||
* Set/update record | ||
* @param key {any} | ||
* @param value {any} | ||
* @param [opts] {Object} options object | ||
* @param [opts.silent=false] {boolean} if true no event will be triggered | ||
* @param [opts.life=0] {number} seconds of life. If 0 not expire. | ||
* @returns {{isNew: boolean, createdOn: Date|null, updatedOn: Date|null, value: *}} | ||
* @example | ||
* incache.set('my key', 'my value'); | ||
* incache.set('my object', {a: 1, b: 2}); | ||
* incache.set('my boolean', true, {life: 2}); // Expires after 2 seconds | ||
*/ | ||
incache.set = (key, value, opts = {}) => { | ||
let record = { | ||
isNew: true, | ||
createdOn: null, | ||
updatedOn: null, | ||
expiresOn: null, | ||
value: value | ||
}; | ||
this.setConfig(opts); | ||
} | ||
opts = helper.defaults(opts, DEFAULT_OPTS); | ||
_write() { | ||
let {config, data} = this._memory; | ||
if (config.save) { | ||
let content = JSON.stringify(data); | ||
fs.writeFileSync(config.filePath, content); | ||
} | ||
} | ||
if (opts.life && helper.is(opts.life, 'number')) { | ||
record.expiresOn = helper.addSecondsToNow(opts.life); | ||
_read() { | ||
let config = this._memory.config; | ||
if (config.save && fs.existsSync(config.filePath)) { | ||
let content = fs.readFileSync(config.filePath); | ||
try { | ||
this._storage = this._memory.data = JSON.parse(content); | ||
} catch (e) { | ||
this._storage = this._memory.data = {}; | ||
} | ||
} | ||
} | ||
if (incache.has(key)) { | ||
record.isNew = false; | ||
record.updatedOn = new Date(); | ||
if (!opts.silent) | ||
_onUpdated.call(this, key, record); | ||
} else { | ||
record.createdOn = new Date(); | ||
if (!opts.silent) | ||
_onCreated.call(this, key, record); | ||
/** | ||
* Set configuration | ||
* @param [opts] {Object} configuration object | ||
* @param [opts.save=true] {boolean} if true saves cache in disk. (server only) | ||
* @param [opts.filePath=.incache] {string} cache file path | ||
* @param [opts.storeName] {string} store name | ||
* @param [opts.global] {Object} global record configuration | ||
* @param [opts.global.silent=false] {boolean} if true no event will be triggered | ||
* @param [opts.global.life=0] {number} max age. If 0 not expire | ||
*/ | ||
setConfig(opts = {}) { | ||
if (opts.storeName) | ||
this.GLOBAL_KEY += opts.storeName; | ||
if (!this._root[this.GLOBAL_KEY]) { | ||
this._root[this.GLOBAL_KEY] = { | ||
metadata: { | ||
lastSave: null | ||
}, | ||
data: {}, | ||
config: this.DEFAULT_CONFIG | ||
}; | ||
} | ||
this._root[this.GLOBAL_KEY].config = helper.defaults(opts, this.DEFAULT_CONFIG); | ||
this._memory = this._root[this.GLOBAL_KEY]; | ||
this._storage = this._memory.data; | ||
if (helper.isServer()) | ||
this._read(); | ||
} | ||
storage[key] = record; | ||
/** | ||
* Get configuration | ||
* @returns {*} | ||
*/ | ||
getConfig() { | ||
return this._memory.config; | ||
} | ||
return record; | ||
}; | ||
/** | ||
* Set/update record | ||
* @param key {any} | ||
* @param value {any} | ||
* @param [opts] {Object} options object | ||
* @param [opts.silent=false] {boolean} if true no event will be triggered. (overwrites global configuration) | ||
* @param [opts.life=0] {number} max age. If 0 not expire. (overwrites global configuration) | ||
* @returns {{isNew: boolean, createdOn: Date|null, updatedOn: Date|null, value: *}} | ||
* @example | ||
* inCache.set('my key', 'my value'); | ||
* inCache.set('my object', {a: 1, b: 2}); | ||
* inCache.set('my boolean', true, {life: 2}); // Expires after 2 seconds | ||
*/ | ||
set(key, value, opts = {}) { | ||
let record = { | ||
isNew: true, | ||
createdOn: null, | ||
updatedOn: null, | ||
expiresOn: null, | ||
value: value | ||
}; | ||
/** | ||
* Set/update multiple records. This method not trigger any event. | ||
* @param records {array} array of object, e.g. [{key: foo1, value: bar1},{key: foo2, value: bar2}] | ||
* @example | ||
* incache.bulkSet([ | ||
* {key: 'my key 1', value: 'my value 1'}, | ||
* {key: 'my key 2', value: 'my value 2'}, | ||
* {key: 'my key 3', value: 'my value 3'}, | ||
* {key: 'my key 4', value: 'my value 4'} | ||
* ]); | ||
*/ | ||
incache.bulkSet = (records) => { | ||
if (!helper.is(records, 'array')) | ||
throw new Error('records must be an array of object, e.g. {key: foo, value: bar}'); | ||
opts = helper.defaults(opts, this.DEFAULT_CONFIG.global); | ||
for (let i = 0; i < records.length; i++) { | ||
if (helper.is(records[i].key, 'undefined') || helper.is(records[i].value, 'undefined')) | ||
throw new Error('key and value properties are required'); | ||
incache.set(records[i].key, records[i].value, {silent: true}); | ||
if (opts.life && helper.is(opts.life, 'number')) { | ||
record.expiresOn = helper.addSecondsToNow(opts.life); | ||
} | ||
if (this.has(key)) { | ||
record.isNew = false; | ||
record.updatedOn = new Date(); | ||
if (!opts.silent) | ||
this._onUpdated.call(this, key, record); | ||
} else { | ||
record.createdOn = new Date(); | ||
if (!opts.silent) | ||
this._onCreated.call(this, key, record); | ||
} | ||
this._storage[key] = record; | ||
return record; | ||
} | ||
}; | ||
/** | ||
* Get record by key | ||
* @param key {any} | ||
* @param [onlyValue=true] {boolean} if false get incache record | ||
* @returns {any|null} | ||
* @example | ||
* incache.get('my key'); | ||
*/ | ||
incache.get = (key, onlyValue = true) => { | ||
if (incache.has(key)) { | ||
if (incache.expired(key)) { | ||
incache.remove(key, true); | ||
/** | ||
* Set/update multiple records. This method not trigger any event. | ||
* @param records {array} array of object, e.g. [{key: foo1, value: bar1},{key: foo2, value: bar2}] | ||
* @example | ||
* inCache.bulkSet([ | ||
* {key: 'my key 1', value: 'my value 1'}, | ||
* {key: 'my key 2', value: 'my value 2'}, | ||
* {key: 'my key 3', value: 'my value 3'}, | ||
* {key: 'my key 4', value: 'my value 4'} | ||
* ]); | ||
*/ | ||
bulkSet(records) { | ||
if (!helper.is(records, 'array')) | ||
throw new Error('records must be an array of object, e.g. {key: foo, value: bar}'); | ||
for (let i = 0; i < records.length; i++) { | ||
if (helper.is(records[i].key, 'undefined') || helper.is(records[i].value, 'undefined')) | ||
throw new Error('key and value properties are required'); | ||
this.set(records[i].key, records[i].value, {silent: true, fromBulk: true}); | ||
} | ||
} | ||
/** | ||
* Get record by key | ||
* @param key {any} | ||
* @param [onlyValue=true] {boolean} if false get InCache record | ||
* @returns {any|null} | ||
* @example | ||
* inCache.get('my key'); | ||
*/ | ||
get(key, onlyValue = true) { | ||
if (this.has(key)) { | ||
if (this.expired(key)) { | ||
this.remove(key, true); | ||
return null; | ||
} | ||
return onlyValue ? this._storage[key].value : this._storage[key]; | ||
} else { | ||
return null; | ||
} | ||
return onlyValue ? storage[key].value : storage[key]; | ||
} else { | ||
return null; | ||
} | ||
}; | ||
/** | ||
* Delete a record | ||
* @param key {any} | ||
* @param [silent=false] {boolean} if true no event will be triggered | ||
* @example | ||
* incache.remove('my key'); | ||
*/ | ||
incache.remove = (key, silent = false) => { | ||
delete storage[key]; | ||
if (!silent) | ||
_onRemoved.call(this, key); | ||
}; | ||
/** | ||
* Delete a record | ||
* @param key {any} | ||
* @param [silent=false] {boolean} if true no event will be triggered | ||
* @param [opts] {Object} optional arguments | ||
* @example | ||
* inCache.remove('my key'); | ||
*/ | ||
remove(key, silent = false, opts = {}) { | ||
delete this._storage[key]; | ||
if (!silent) | ||
this._onRemoved.call(this, key); | ||
} | ||
/** | ||
* Delete multiple records | ||
* @param keys {array} an array of keys | ||
* @example | ||
* incache.bulkRemove(['key1', 'key2', 'key3']); | ||
*/ | ||
incache.bulkRemove = (keys) => { | ||
if (!helper.is(keys, 'array')) | ||
throw new Error('keys must be an array of keys'); | ||
/** | ||
* Given a key that has value like an array adds value to end of array | ||
* @param key {any} | ||
* @param value {any} | ||
* @returns {*} | ||
* @example | ||
* inCache.set('myArray', ['hello', 'world']); | ||
* inCache.addTo('myArray', 'ciao'); //-> ['hello', 'world', 'ciao']; | ||
*/ | ||
addTo(key, value) { | ||
if (!this.has(key)) return null; | ||
let record = this.get(key); | ||
for (let i = 0; i < keys.length; i++) { | ||
incache.remove(keys[i], true); | ||
if (!helper.is(record, 'array')) | ||
throw new Error('object must be an array'); | ||
record.push(value); | ||
return this.set(key, record); | ||
} | ||
}; | ||
/** | ||
* Fetch all records | ||
* @returns {Array} | ||
*/ | ||
incache.all = () => { | ||
let records = []; | ||
/** | ||
* Given a key that has value like an array adds value to beginning of array | ||
* @param key {any} | ||
* @param value {any} | ||
* @returns {*} | ||
* @example | ||
* inCache.set('myArray', ['hello', 'world']); | ||
* inCache.prependTo('myArray', 'ciao'); //-> ['ciao', 'hello', 'world']; | ||
*/ | ||
prependTo(key, value) { | ||
if (!this.has(key)) return null; | ||
let record = this.get(key); | ||
for (let key in storage) { | ||
if (storage.hasOwnProperty(key)) { | ||
if(incache.expired(key)) { | ||
incache.remove(key, true); | ||
} else { | ||
records.push({ | ||
key: key, | ||
value: storage[key].value | ||
}); | ||
if (!helper.is(record, 'array')) | ||
throw new Error('object must be an array'); | ||
record.unshift(value); | ||
return this.set(key, record); | ||
} | ||
/** | ||
* Given a key that has value like an array updates key(s) if `where` is satisfied | ||
* @param key {any} | ||
* @param value {any} | ||
* @param where {any} | ||
* @example | ||
* inCache.set('myArray', ['hello', 'world']); | ||
* inCache.updateIn('myArray', 'ciao', 'hello'); //-> ['ciao', 'world']; | ||
* | ||
* inCache.set('myArray', [{a: 1, b: 2, c: 3], {b: 2, c: 3}, {b: 4, e: 5}); | ||
* inCache.updateIn('myArray', {z: 0, x: 0}, {b: 2, c: 3}); //-> [{z: 0, x: 0}, {z: 0, x: 0}, {b: 4, e: 5}]; | ||
*/ | ||
updateIn(key, value, where) { | ||
if (!this.has(key)) return null; | ||
if (helper.is(value, 'undefined')) | ||
throw new Error('value cannot be undefined'); | ||
if (helper.is(where, 'undefined')) | ||
throw new Error('where cannot be undefined'); | ||
let recordValue = this.get(key); | ||
if (!helper.is(recordValue, 'array')) | ||
throw new Error('value must be an array'); | ||
let updated = false; | ||
for (let i in recordValue) { | ||
if (recordValue.hasOwnProperty(i)) { | ||
let result = []; | ||
for (let prop in where) { | ||
if (where.hasOwnProperty(prop)) | ||
if (helper.is(where, 'object')) | ||
result.push(typeof recordValue[i][prop] !== 'undefined' && recordValue[i][prop] === where[prop]); | ||
else | ||
result.push(recordValue[i] === where); | ||
} | ||
if (result.length && result.indexOf(false) === -1) { | ||
updated = true; | ||
recordValue[i] = value; | ||
} | ||
} | ||
} | ||
if (updated) { | ||
this.set(key, recordValue); | ||
} | ||
} | ||
return records; | ||
}; | ||
/** | ||
* Given a key that has value like an array removes key(s) if `where` is satisfied | ||
* @param key {any} | ||
* @param where {any} | ||
* @example | ||
* inCache.set('myArray', ['hello', 'world']); | ||
* inCache.removeFrom('myArray', 'hello'); //-> ['world']; | ||
*/ | ||
removeFrom(key, where) { | ||
if (!this.has(key)) return null; | ||
/** | ||
* Check if record is expired | ||
* @param key {any} | ||
* @returns {boolean} | ||
*/ | ||
incache.expired = (key) => { | ||
if (storage[key] && storage[key].expiresOn) { | ||
let now = new Date(); | ||
let expiry = new Date(storage[key].expiresOn); | ||
return now > expiry; | ||
} else { | ||
return false; | ||
if (helper.is(where, 'undefined')) | ||
throw new Error('where cannot be undefined'); | ||
let recordValue = this.get(key); | ||
if (!helper.is(recordValue, 'array')) | ||
throw new Error('value must be an array'); | ||
let recordLengthBefore = recordValue.length; | ||
for (let i in recordValue) { | ||
if (recordValue.hasOwnProperty(i)) { | ||
let result = []; | ||
for (let prop in where) { | ||
if (where.hasOwnProperty(prop)) | ||
if (helper.is(where, 'object')) | ||
result.push(typeof recordValue[i][prop] !== 'undefined' && recordValue[i][prop] === where[prop]); | ||
else | ||
result.push(recordValue[i] === where); | ||
} | ||
if (result.length && result.indexOf(false) === -1) | ||
recordValue.splice(i, 1); | ||
} | ||
} | ||
if (recordLengthBefore !== recordValue.length) { | ||
this.set(key, recordValue); | ||
} | ||
} | ||
}; | ||
/** | ||
* Remove all records | ||
*/ | ||
incache.clear = () => { | ||
/** | ||
* Reset object | ||
* @ignore | ||
* Delete multiple records | ||
* @param keys {array} an array of keys | ||
* @example | ||
* inCache.bulkRemove(['key1', 'key2', 'key3']); | ||
*/ | ||
storage = root[GLOBAL_KEY] = {}; | ||
}; | ||
bulkRemove(keys) { | ||
if (!helper.is(keys, 'array')) | ||
throw new Error('keys must be an array of keys'); | ||
/** | ||
* Check if key exists | ||
* @param key {any} | ||
* @returns {boolean} | ||
* @example | ||
* incache.has('my key'); | ||
*/ | ||
incache.has = (key) => { | ||
return storage.hasOwnProperty(key); | ||
}; | ||
for (let i = 0; i < keys.length; i++) { | ||
this.remove(keys[i], true, {fromBulk: true}); | ||
} | ||
} | ||
/** | ||
* Triggered when a record has been deleted | ||
* @param callback {incache.onRemoved~removedCallback} callback function | ||
* @example | ||
* incache.onRemoved((key)=>{ | ||
* console.log('removed', key); | ||
* }); | ||
*/ | ||
incache.onRemoved = (callback) => { | ||
_onRemoved = callback; | ||
}; | ||
/** | ||
* Fetch all records | ||
* @returns {Array} | ||
*/ | ||
all() { | ||
let records = []; | ||
/** | ||
* onRemoved callback | ||
* @callback incache.onRemoved~removedCallback | ||
* @param key {string} key of record removed | ||
*/ | ||
for (let key in this._storage) { | ||
if (this._storage.hasOwnProperty(key)) { | ||
if (this.expired(key)) { | ||
this.remove(key, true); | ||
} else { | ||
records.push({ | ||
key: key, | ||
value: this._storage[key].value | ||
}); | ||
} | ||
} | ||
} | ||
/** | ||
* Triggered when a record has been created | ||
* @param callback {incache.onCreated~createdCallback} callback function | ||
* @example | ||
* incache.onCreated((key, record)=>{ | ||
* console.log('created', key, record); | ||
* }); | ||
*/ | ||
incache.onCreated = (callback) => { | ||
_onCreated = callback; | ||
}; | ||
return records; | ||
} | ||
/** | ||
* onCreated callback | ||
* @callback incache.onCreated~createdCallback | ||
* @param key {string} key of record created | ||
* @param record {Object} record object | ||
*/ | ||
/** | ||
* Check if record is expired | ||
* @param key {any} | ||
* @returns {boolean} | ||
*/ | ||
expired(key) { | ||
if (this._storage[key] && this._storage[key].expiresOn) { | ||
let now = new Date(); | ||
let expiry = new Date(this._storage[key].expiresOn); | ||
return now > expiry; | ||
} else { | ||
return false; | ||
} | ||
} | ||
/** | ||
* Triggered when a record has been updated | ||
* @param callback {incache.onUpdated~updatedCallback} callback function | ||
* @example | ||
* incache.onUpdated((key, record)=>{ | ||
* console.log('updated', key, record); | ||
* }); | ||
*/ | ||
incache.onUpdated = (callback) => { | ||
_onUpdated = callback; | ||
}; | ||
/** | ||
* Remove all records | ||
*/ | ||
clear() { | ||
/** | ||
* Reset object | ||
* @ignore | ||
*/ | ||
this._storage = this._memory.data = {}; | ||
} | ||
/** | ||
* onUpdated callback | ||
* @callback incache.onUpdated~updatedCallback | ||
* @param key {string} key of record updated | ||
* @param record {Object} record object | ||
*/ | ||
/** | ||
* Check if key exists | ||
* @param key {any} | ||
* @returns {boolean} | ||
* @example | ||
* inCache.has('my key'); | ||
*/ | ||
has(key) { | ||
return this._storage.hasOwnProperty(key); | ||
} | ||
/** | ||
* Triggered when a record has been deleted | ||
* @param callback {InCache~removedCallback} callback function | ||
* @example | ||
* inCache.onRemoved((key)=>{ | ||
* console.log('removed', key); | ||
* }); | ||
*/ | ||
onRemoved(callback) { | ||
this._onRemoved = callback; | ||
} | ||
/** | ||
* onRemoved callback | ||
* @callback InCache~removedCallback | ||
* @param key {string} key of record removed | ||
*/ | ||
/** | ||
* Triggered when a record has been created | ||
* @param callback {InCache~createdCallback} callback function | ||
* @example | ||
* inCache.onCreated((key, record)=>{ | ||
* console.log('created', key, record); | ||
* }); | ||
*/ | ||
onCreated(callback) { | ||
this._onCreated = callback; | ||
} | ||
/** | ||
* onCreated callback | ||
* @callback InCache~createdCallback | ||
* @param key {string} key of record created | ||
* @param record {Object} record object | ||
*/ | ||
/** | ||
* Triggered when a record has been updated | ||
* @param callback {InCache~updatedCallback} callback function | ||
* @example | ||
* inCache.onUpdated((key, record)=>{ | ||
* console.log('updated', key, record); | ||
* }); | ||
*/ | ||
onUpdated(callback) { | ||
this._onUpdated = callback; | ||
} | ||
/** | ||
* onUpdated callback | ||
* @callback InCache~updatedCallback | ||
* @param key {string} key of record updated | ||
* @param record {Object} record object | ||
*/ | ||
} | ||
/** | ||
* Expose module | ||
*/ | ||
module.exports = incache; | ||
module.exports._global_key = GLOBAL_KEY; | ||
module.exports = InCache; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
118978
20
1328
94
15
1
1