UNPKG

graph-crdt

Version:
388 lines (294 loc) 10.2 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _regenerator = require('babel-runtime/regenerator'); var _regenerator2 = _interopRequireDefault(_regenerator); var _iterator4 = require('babel-runtime/core-js/symbol/iterator'); var _iterator5 = _interopRequireDefault(_iterator4); var _defineProperty2 = require('babel-runtime/helpers/defineProperty'); var _defineProperty3 = _interopRequireDefault(_defineProperty2); var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); var _getIterator2 = require('babel-runtime/core-js/get-iterator'); var _getIterator3 = _interopRequireDefault(_getIterator2); var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _inherits2 = require('babel-runtime/helpers/inherits'); var _inherits3 = _interopRequireDefault(_inherits2); var _symbol = require('babel-runtime/core-js/symbol'); var _symbol2 = _interopRequireDefault(_symbol); var _eventemitter = require('eventemitter3'); var _eventemitter2 = _interopRequireDefault(_eventemitter); var _Node = require('../Node'); var _Node2 = _interopRequireDefault(_Node); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * @module graph-crdt.Graph */ var nodes = (0, _symbol2.default)('graph node container'); /** * Container and interface for groups of nodes. * * @class Graph */ var Graph = function (_Emitter) { (0, _inherits3.default)(Graph, _Emitter); (0, _createClass3.default)(Graph, null, [{ key: 'create', /** * Instantiates a graph without needing `new`. * * @returns {Graph} - A graph instance. */ value: function create() { return new Graph(); } /** * Imports a format compliant graph into a new one. It expects * nested nodes to use the node metadata format. Useful for * sending and importing graphs over the network. * * @param {Object} object - The raw graph object. * @returns {Graph} - A new graph instance that consumes * the imported data. */ }, { key: 'source', value: function source(object) { // Create a new graph var graph = Graph.create(); // For each node... (0, _keys2.default)(object).forEach(function (key) { var node = object[key]; // Make sure it's a node. if (!(node instanceof _Node2.default)) { node = _Node2.default.source(node); } // Get it's unique ID. var _node$meta = node.meta(), uid = _node$meta.uid; // Add it to the new graph. graph[nodes][uid] = node; }); return graph; } }]); function Graph() { (0, _classCallCheck3.default)(this, Graph); var _this = (0, _possibleConstructorReturn3.default)(this, (Graph.__proto__ || (0, _getPrototypeOf2.default)(Graph)).call(this)); _this[nodes] = {}; return _this; } /** * Return the unmodified value of a node lookup. * * @param {String} key - The name/uid of the node. * @returns {Node|null} - The node if found, otherwise `null`. */ (0, _createClass3.default)(Graph, [{ key: 'value', value: function value(key) { return this[nodes][key] || null; } }, { key: 'new', value: function _new() { return new Graph(); } /** * Replays all the changes in the graph as though they occurred * after the events in the target graph. * @param {Graph} target - Preceding state. * @return {Graph} - A new graph containing the rebased nodes. */ }, { key: 'rebase', value: function rebase(target) { var rebased = this.new(); rebased.merge(target); rebased.merge(this); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = (0, _getIterator3.default)(this), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _step$value = (0, _slicedToArray3.default)(_step.value, 1), id = _step$value[0]; var existing = target.value(id); if (existing) { rebased[nodes][id] = this.value(id).rebase(existing); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return rebased; } /** * Figures out what fields are common to both graphs. * @param {Graph} target - Any other graph. * @return {Graph} - The shared properties between both graphs. */ }, { key: 'overlap', value: function overlap(target) { var shared = this.new(); var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = (0, _getIterator3.default)(this), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var _step2$value = (0, _slicedToArray3.default)(_step2.value, 1), key = _step2$value[0]; if (this.value(key) && target.value(key)) { // Calculate the node overlap. var nodeSource = this.value(key); var nodeTarget = target.value(key); var overlap = nodeSource.overlap(nodeTarget); // Merge it into the new graph. shared.merge((0, _defineProperty3.default)({}, key, overlap)); } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } return shared; } /** * Merge one graph with another (graph union operation). * * @param {Object} graph - The graph to merge with. * Items must be enumerable, and cannot be inherited from prototypes. * @returns {Graph} - The `this` context. */ }, { key: 'merge', value: function merge(graph) { /** Ensure it's a graph. */ if (!(graph instanceof Graph)) { graph = Graph.source(graph); } var changes = { update: this.new(), history: this.new() }; var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = (0, _getIterator3.default)(graph), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var _step3$value = (0, _slicedToArray3.default)(_step3.value, 2), uid = _step3$value[0], node = _step3$value[1]; var target = this.value(uid); if (!target) { target = this[nodes][uid] = node.new(); } var _target$merge = target.merge(node), update = _target$merge.update, history = _target$merge.history; changes.update[nodes][uid] = update; changes.history[nodes][uid] = history; } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } this.emit('update', changes.update); this.emit('history', changes.history); return changes; } /** * Iterates over every node in the graph. * @return {Array} - Every yielded value is a key/value pair. */ }, { key: _iterator5.default, value: _regenerator2.default.mark(function value() { var object, key, value; return _regenerator2.default.wrap(function value$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: object = this[nodes]; _context.t0 = _regenerator2.default.keys(object); case 2: if ((_context.t1 = _context.t0()).done) { _context.next = 10; break; } key = _context.t1.value; if (!object.hasOwnProperty(key)) { _context.next = 8; break; } value = this.value(key); _context.next = 8; return [key, value]; case 8: _context.next = 2; break; case 10: case 'end': return _context.stop(); } } }, value, this); }) /* Coercion interfaces */ /** * Used to serialize a graph (JSON.stringify calls this method). * * @private * @returns {Object} - The hidden collection of nodes. */ }, { key: 'toJSON', value: function toJSON() { return this[nodes]; } }]); return Graph; }(_eventemitter2.default); exports.default = Graph;