vulpes
Version:
Job management framework
298 lines (241 loc) • 12.4 kB
JavaScript
"use strict";
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; }; }();
var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var objectStream = require("@kiosked/object-stream");
var filterStream = require("stream-filter");
var Storage = require("./Storage.js");
var _require = require("../streams.js"),
waitForStream = _require.waitForStream;
var _require2 = require("../cloning.js"),
clone = _require2.clone;
/**
* @typedef {Object} MemoryStorageOptions
* @property {FileStorage|null} fileStorage Optional filestorage reference
* for sync'ing memory -> file system
* @property {Number=} flushDelay Delay in milliseconds between flushing
* the memory to disk
*/
/**
* Memory storage adapter
* Stores jobs in memory. Once application is closed all jobs are
* purged - do not use this storage if you desire persistence.
* @augments Storage
* @memberof module:Vulpes
*/
var MemoryStorage = function (_Storage) {
_inherits(MemoryStorage, _Storage);
/**
* Constructor for the storage instance
* @param {MemoryStorageOptions=} opts Optional configuration for
* the memory storage instance
*/
function MemoryStorage() {
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
_classCallCheck(this, MemoryStorage);
var _this = _possibleConstructorReturn(this, (MemoryStorage.__proto__ || Object.getPrototypeOf(MemoryStorage)).call(this));
_this._store = {};
var _opts$fileStorage = opts.fileStorage,
fileStorage = _opts$fileStorage === undefined ? null : _opts$fileStorage,
_opts$flushDelay = opts.flushDelay,
flushDelay = _opts$flushDelay === undefined ? 5000 : _opts$flushDelay;
_this._fileStorage = fileStorage;
_this._dirty = [];
_this._flushing = false;
_this._flushDelay = flushDelay;
return _this;
}
/**
* The job store
* @type {Object}
* @readonly
* @memberof MemoryStorage
*/
_createClass(MemoryStorage, [{
key: "getItem",
/**
* Get an item's value
* @param {String} key Get the value of a key
* @returns {Promise.<*|null>} A promise that resolves with the value of
* the key or null if not found
* @memberof MemoryStorage
*/
value: function getItem(key) {
var value = this.store[key] || null;
if (value !== null) {
value = clone(value);
}
return Promise.resolve(value);
}
/**
* Initialise the memory storage
* @returns {Promise}
* @memberof MemoryStorage
*/
}, {
key: "initialise",
value: function () {
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
var _this2 = this;
var itemStream;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return _get(MemoryStorage.prototype.__proto__ || Object.getPrototypeOf(MemoryStorage.prototype), "initialise", this).call(this);
case 2:
if (!this._fileStorage) {
_context.next = 11;
break;
}
_context.next = 5;
return this._fileStorage.initialise();
case 5:
_context.next = 7;
return this._fileStorage.streamItems();
case 7:
itemStream = _context.sent;
itemStream.on("data", function (item) {
_this2.store[item.id] = item;
});
_context.next = 11;
return waitForStream(itemStream);
case 11:
case "end":
return _context.stop();
}
}
}, _callee, this);
}));
function initialise() {
return _ref.apply(this, arguments);
}
return initialise;
}()
/**
* Remove an item from the memory store
* @param {String} key The key of the item to remove
* @returns {Promise} A promise that resolves once the key has been removed
* @memberof MemoryStorage
*/
}, {
key: "removeItem",
value: function removeItem(key) {
this.store[key] = null;
delete this.store[key];
this._flagDirtyKey(key);
return Promise.resolve();
}
/**
* Set an item in the memory store
* @param {String} key The key to store under
* @param {Object} value The value to store
* @returns {Promise} Returns a promise that resolves once the item has
* been stored
* @memberof MemoryStorage
*/
}, {
key: "setItem",
value: function setItem(key, value) {
this.store[key] = clone(value);
this._flagDirtyKey(key);
return Promise.resolve();
}
/**
* Stream all items
* @returns {Promise.<ReadableStream>} A promise that resolves with the readable stream
* @memberof MemoryStorage
*/
}, {
key: "streamItems",
value: function streamItems() {
var _this3 = this;
return new Promise(function (resolve) {
resolve(objectStream.fromArray(Object.keys(_this3.store).map(function (key) {
return clone(_this3.store[key]);
})).pipe(filterStream.obj(function (item) {
return !!item;
})));
});
}
}, {
key: "_flagDirtyKey",
value: function _flagDirtyKey(key) {
var _this4 = this;
// Skip if no filestorage is used
if (this._fileStorage === null) return;
if (this._dirty.includes(key) === false) {
this._dirty.push(key);
}
setTimeout(function () {
return _this4._flushDirty();
}, 0);
}
}, {
key: "_flushDirty",
value: function () {
var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
var _this5 = this;
var dirtyKeys, items;
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
if (!this._flushing) {
_context2.next = 2;
break;
}
return _context2.abrupt("return");
case 2:
this._flushing = true;
dirtyKeys = [].concat(_toConsumableArray(this._dirty));
this._dirty = [];
items = dirtyKeys.reduce(function (output, key) {
return Object.assign(output, _defineProperty({}, key, _this5.store[key] ? clone(_this5.store[key]) : null));
}, {});
_context2.prev = 6;
_context2.next = 9;
return this._fileStorage.setItems(items);
case 9:
_context2.next = 15;
break;
case 11:
_context2.prev = 11;
_context2.t0 = _context2["catch"](6);
console.error(_context2.t0);
// Put back marked keys
this._dirty = [].concat(_toConsumableArray(new Set([].concat(_toConsumableArray(this._dirty), _toConsumableArray(dirtyKeys)))));
case 15:
setTimeout(function () {
_this5._flushing = false;
if (_this5._dirty.length > 0) {
_this5._flushDirty();
}
}, this._flushDelay);
case 16:
case "end":
return _context2.stop();
}
}
}, _callee2, this, [[6, 11]]);
}));
function _flushDirty() {
return _ref2.apply(this, arguments);
}
return _flushDirty;
}()
}, {
key: "store",
get: function get() {
return this._store;
}
}]);
return MemoryStorage;
}(Storage);
module.exports = MemoryStorage;