UNPKG

semantic-network

Version:

A utility library for manipulating a list of links that form a semantic interface to a network of resources.

418 lines (417 loc) 23.4 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Differencer = void 0; var anylogger_1 = __importDefault(require("anylogger")); var semantic_link_1 = require("semantic-link"); var linkRelation_1 = require("../linkRelation"); var instanceOfCollection_1 = require("../utils/instanceOf/instanceOfCollection"); var lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep")); var defaultEqualityOperators_1 = require("../utils/comparators/defaultEqualityOperators"); var log = (0, anylogger_1.default)('test'); var noopResolver = function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, undefined]; }); }); }; var noopVoidResolver = function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/]; }); }); }; var Differencer = /** @class */ (function () { function Differencer() { } Object.defineProperty(Differencer, "defaultEqualityOperators", { /** * A default set of comparisons made to check if two resource * representation refer to the same resource in a collection. * * The most specific and robust equality check is first, with the most vague and * optimistic last. */ get: function () { return defaultEqualityOperators_1.defaultEqualityOperators; }, enumerable: false, configurable: true }); /** * Processes difference sets (create, update, delete) for between two client-side collections {@Link CollectionRepresentation} * * WARNING: this is a differencing set and update can be misleading. What is means is that we have 'matched' these * two resource as both 'existing' and thus may or may not require some form of update on them. The decision on * whether there is an actual difference is up to some other decision that know about the internals of the resource * (such as an edit form merger). * * set one - current collection * set two - new collection * add/create - not in set one but in set two * match/update - intersection of both sets (may or may not require a change) * remove/delete - in set one and not in set two * * set one: current * +-----------------------------+ * +-----------------------------------------+ * | | | | * | add | match | remove | * | | | | * +-----------------------------------------+ * +--------------------------+ * set two: new * * * @param resource an existing resource collection that is * synchronised with the server (network of data). * * @param document a document with a collection CollectionRepresentation * format that describes the state of the resources. * * @param options a document with a collection CollectionRepresentation * format that describes the state of the resources. */ Differencer.difference = function (resource, document, options) { var _a, e_1, _b, _c, _d, e_2, _e, _f, _g, e_3, _h, _j; return __awaiter(this, void 0, void 0, function () { var _k, _l, createStrategy, _m, updateStrategy, _o, deleteStrategy, _p, comparators, updateItems, deleteItems, createItems, _loop_1, comparators_1, comparators_1_1, comparator, _q, batchSize, _r, deleteItems_1, deleteItems_1_1, item, e_1_1, _s, updateItems_1, updateItems_1_1, item, e_2_1, createResults, _t, createItems_1, createItems_1_1, item, resource_1, e_3_1, infos; var e_4, _u; var _this = this; return __generator(this, function (_v) { switch (_v.label) { case 0: if (!(0, instanceOfCollection_1.instanceOfCollection)(resource)) { throw new Error("[Differencer] collection resource '".concat(semantic_link_1.LinkUtil.getUri(resource, linkRelation_1.LinkRelation.Self), "' has no items")); } if (!(0, instanceOfCollection_1.instanceOfCollection)(document)) { throw new Error("[Differencer] collection document '".concat(semantic_link_1.LinkUtil.getUri(document, linkRelation_1.LinkRelation.Self), "' has no items")); } _k = __assign({}, options), _l = _k.createStrategy, createStrategy = _l === void 0 ? noopResolver : _l, _m = _k.updateStrategy, updateStrategy = _m === void 0 ? noopVoidResolver : _m, _o = _k.deleteStrategy, deleteStrategy = _o === void 0 ? noopResolver : _o; _p = __assign({}, options).comparators, comparators = _p === void 0 ? Differencer.defaultEqualityOperators : _p; // provide a default comparator and normalise a single comparator to an array of comparators if (typeof comparators === 'function') { comparators = [comparators]; } updateItems = []; deleteItems = __spreadArray([], __read(resource.items), false); createItems = (0, lodash_clonedeep_1.default)(document.items); _loop_1 = function (comparator) { // Get a list of items that need to be updated. // create an array of indexes, eg // if the first two match return [[0,0]] var itemsToMove = deleteItems .map(function (item, index) { var docIndex = createItems.findIndex(function (createItem) { return comparator(item, createItem); }); return docIndex >= 0 ? { lIndex: index, rIndex: docIndex } : undefined; }) .filter(function (item) { return !!item; }); // Remove those items that are to be updated from the 'delete' list // on any that are removed, add reference for later processing onto the pair // if there is a match return [0,0,{item}] itemsToMove .sort(function (lVal, rVal) { return lVal.lIndex - rVal.lIndex; }) .reverse() .forEach(function (pair) { var index = pair.lIndex; // eslint-disable-next-line @typescript-eslint/no-unused-vars var _a = __read(deleteItems.splice(index, 1), 2), representation = _a[0], _ = _a[1]; pair.lVal = representation; }); // Remove those items that are to be updated from the 'create' list // on any that are removed, add reference for later processing onto the pair itemsToMove .sort(function (lVal, rVal) { return lVal.rIndex - rVal.rIndex; }) .reverse() .forEach(function (pair) { var index = pair.rIndex; // eslint-disable-next-line @typescript-eslint/no-unused-vars var _a = __read(createItems.splice(index, 1), 2), representation = _a[0], _ = _a[1]; pair.rVal = representation; }); // Append to the 'update' list the items removed from the 'delete' and 'create' lists var argArray = itemsToMove.map(function (item) { return ({ lVal: item.lVal, rVal: item.rVal }); }); // TODO: use reduce // eslint-disable-next-line prefer-spread updateItems.push.apply(updateItems, argArray); }; try { for (comparators_1 = __values(comparators), comparators_1_1 = comparators_1.next(); !comparators_1_1.done; comparators_1_1 = comparators_1.next()) { comparator = comparators_1_1.value; _loop_1(comparator); } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (comparators_1_1 && !comparators_1_1.done && (_u = comparators_1.return)) _u.call(comparators_1); } finally { if (e_4) throw e_4.error; } } _q = __assign({}, options).batchSize, batchSize = _q === void 0 ? undefined : _q; // // 1. Delete all resource first // log.debug('delete strategy sequential: count \'%s\'', deleteItems.length); _v.label = 1; case 1: _v.trys.push([1, 7, 8, 13]); _r = true, deleteItems_1 = __asyncValues(deleteItems); _v.label = 2; case 2: return [4 /*yield*/, deleteItems_1.next()]; case 3: if (!(deleteItems_1_1 = _v.sent(), _a = deleteItems_1_1.done, !_a)) return [3 /*break*/, 6]; _c = deleteItems_1_1.value; _r = false; item = _c; return [4 /*yield*/, deleteStrategy(item)]; case 4: _v.sent(); _v.label = 5; case 5: _r = true; return [3 /*break*/, 2]; case 6: return [3 /*break*/, 13]; case 7: e_1_1 = _v.sent(); e_1 = { error: e_1_1 }; return [3 /*break*/, 13]; case 8: _v.trys.push([8, , 11, 12]); if (!(!_r && !_a && (_b = deleteItems_1.return))) return [3 /*break*/, 10]; return [4 /*yield*/, _b.call(deleteItems_1)]; case 9: _v.sent(); _v.label = 10; case 10: return [3 /*break*/, 12]; case 11: if (e_1) throw e_1.error; return [7 /*endfinally*/]; case 12: return [7 /*endfinally*/]; case 13: if (!(batchSize === 0 || !batchSize)) return [3 /*break*/, 15]; log.debug('update strategy parallel: count \'%s\'', updateItems.length); return [4 /*yield*/, Promise.all(updateItems.map(function (item) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, updateStrategy(item.lVal, item.rVal)]; case 1: return [2 /*return*/, _a.sent()]; } }); }); }))]; case 14: _v.sent(); return [3 /*break*/, 28]; case 15: log.debug('update strategy sequential: count \'%s\'', updateItems.length); _v.label = 16; case 16: _v.trys.push([16, 22, 23, 28]); _s = true, updateItems_1 = __asyncValues(updateItems); _v.label = 17; case 17: return [4 /*yield*/, updateItems_1.next()]; case 18: if (!(updateItems_1_1 = _v.sent(), _d = updateItems_1_1.done, !_d)) return [3 /*break*/, 21]; _f = updateItems_1_1.value; _s = false; item = _f; return [4 /*yield*/, updateStrategy(item.lVal, item.rVal)]; case 19: _v.sent(); _v.label = 20; case 20: _s = true; return [3 /*break*/, 17]; case 21: return [3 /*break*/, 28]; case 22: e_2_1 = _v.sent(); e_2 = { error: e_2_1 }; return [3 /*break*/, 28]; case 23: _v.trys.push([23, , 26, 27]); if (!(!_s && !_d && (_e = updateItems_1.return))) return [3 /*break*/, 25]; return [4 /*yield*/, _e.call(updateItems_1)]; case 24: _v.sent(); _v.label = 25; case 25: return [3 /*break*/, 27]; case 26: if (e_2) throw e_2.error; return [7 /*endfinally*/]; case 27: return [7 /*endfinally*/]; case 28: createResults = []; if (!(batchSize === 0 || !batchSize)) return [3 /*break*/, 30]; log.debug('create strategy parallel: count \'%s\'', createItems.length); return [4 /*yield*/, Promise.all(createItems .map(function (item) { return __awaiter(_this, void 0, void 0, function () { var resource; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, createStrategy(item)]; case 1: resource = _a.sent(); return [2 /*return*/, { lVal: item, rVal: resource }]; } }); }); }))]; case 29: createResults = _v.sent(); return [3 /*break*/, 43]; case 30: log.debug('create strategy sequential: count \'%s\'', createItems.length); _v.label = 31; case 31: _v.trys.push([31, 37, 38, 43]); _t = true, createItems_1 = __asyncValues(createItems); _v.label = 32; case 32: return [4 /*yield*/, createItems_1.next()]; case 33: if (!(createItems_1_1 = _v.sent(), _g = createItems_1_1.done, !_g)) return [3 /*break*/, 36]; _j = createItems_1_1.value; _t = false; item = _j; return [4 /*yield*/, createStrategy(item)]; case 34: resource_1 = _v.sent(); createResults.push({ lVal: item, rVal: resource_1 }); _v.label = 35; case 35: _t = true; return [3 /*break*/, 32]; case 36: return [3 /*break*/, 43]; case 37: e_3_1 = _v.sent(); e_3 = { error: e_3_1 }; return [3 /*break*/, 43]; case 38: _v.trys.push([38, , 41, 42]); if (!(!_t && !_g && (_h = createItems_1.return))) return [3 /*break*/, 40]; return [4 /*yield*/, _h.call(createItems_1)]; case 39: _v.sent(); _v.label = 40; case 40: return [3 /*break*/, 42]; case 41: if (e_3) throw e_3.error; return [7 /*endfinally*/]; case 42: return [7 /*endfinally*/]; case 43: infos = __spreadArray(__spreadArray([], __read(createResults.map(function (_a) { var lVal = _a.lVal, rVal = _a.rVal; return ({ document: lVal, resource: rVal, action: 'create', }); })), false), __read(updateItems.map(function (_a) { var lVal = _a.lVal, rVal = _a.rVal; return ({ document: lVal, resource: rVal, action: 'update', }); })), false); log.debug('[add, matched, remove] (%s %s %s) on %s', createResults.length, updateItems.length, deleteItems.length, semantic_link_1.LinkUtil.getUri(resource, linkRelation_1.LinkRelation.Self)); return [2 /*return*/, { info: infos, created: createResults, updated: updateItems, deleted: deleteItems, }]; } }); }); }; return Differencer; }()); exports.Differencer = Differencer; //# sourceMappingURL=differencer.js.map