identifi-lib
Version:
Basic tools for reading and writing Identifi messages and identities.
1,064 lines (944 loc) • 37 kB
JavaScript
'use strict';
exports.__esModule = true;
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 _message = require('./message');
var _message2 = _interopRequireDefault(_message);
var _key = require('./key');
var _key2 = _interopRequireDefault(_key);
var _identity = require('./identity');
var _identity2 = _interopRequireDefault(_identity);
var _attribute = require('./attribute');
var _attribute2 = _interopRequireDefault(_attribute);
var _util = require('./util');
var _util2 = _interopRequireDefault(_util);
var _gun = require('gun');
var _gun2 = _interopRequireDefault(_gun);
var _then = require('gun/lib/then');
var _then2 = _interopRequireDefault(_then);
var _load = require('gun/lib/load');
var _load2 = _interopRequireDefault(_load);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // eslint-disable-line no-unused-vars
// eslint-disable-line no-unused-vars
// eslint-disable-line no-unused-vars
var GUN_TIMEOUT = 100;
// temp method for GUN search
async function searchText(node, callback, query, limit, cursor, desc) {
var seen = {};
node.map(function (value, key) {
var cursorCheck = !cursor || desc && key < cursor || !desc && key > cursor;
if (cursorCheck && key.indexOf(query) === 0) {
if (typeof limit === 'number' && Object.keys(seen).length >= limit) {
return;
}
if (seen.hasOwnProperty(key)) {
return;
}
if (value && Object.keys(value).length > 1) {
seen[key] = true;
callback({ value: value, key: key });
}
}
});
}
// TODO: flush onto IPFS
/**
* Identifi index root. Contains five indexes: identitiesBySearchKey, identitiesByTrustDistance,
* messagesByHash, messagesByTimestamp, messagesByDistance. If you want messages saved to IPFS, pass
* options.ipfs = instance.
*
* When you use someone else's index, initialise it using the Index constructor
* @param {Object} gun gun node that contains an Identifi index (e.g. user.get('identifi'))
* @param {Object} options see default options in example
* @example
* Default options:
*{
* ipfs: undefined,
* indexSync: {
* importOnAdd: {
* enabled: true,
* maxMsgCount: 500,
* maxMsgDistance: 2
* },
* subscribe: {
* enabled: true,
* maxMsgDistance: 1
* },
* query: {
* enabled: true
* },
* msgTypes: {
* all: false,
* rating: true,
* verification: true,
* unverification: true
* }
* }
*}
* @returns {Index} Identifi index object
*/
var Index = function () {
function Index(gun, options) {
var _this = this;
_classCallCheck(this, Index);
this.gun = gun || new _gun2.default();
this.options = Object.assign({
indexSync: {
importOnAdd: {
enabled: true,
maxMsgCount: 100,
maxMsgDistance: 2
},
subscribe: {
enabled: true,
maxMsgDistance: 1
},
query: {
enabled: true
},
msgTypes: {
all: false,
rating: true,
verification: true,
unverification: true
}
}
}, options);
if (options.viewpoint) {
this.viewpoint = options.viewpoint;
} else {
this.gun.get('viewpoint').on(function (val, key, msg, eve) {
if (val) {
_this.viewpoint = new _attribute2.default(val);
eve.off();
}
});
}
if (this.options.indexSync.subscribe.enabled) {
setTimeout(function () {
_this.gun.get('trustedIndexes').map().once(function (val, uri) {
if (val) {
// TODO: only get new messages?
_this.gun.user(uri).get('identifi').get('messagesByDistance').map(function (val, key) {
var d = Number.parseInt(key.split(':')[0]);
console.log('got msg with d', d, key);
if (!isNaN(d) && d <= _this.options.indexSync.subscribe.maxMsgDistance) {
_message2.default.fromSig(val).then(function (msg) {
console.log('adding msg ' + msg.hash + ' from trusted index');
if (_this.options.indexSync.msgTypes.all || _this.options.indexSync.msgTypes.hasOwnProperty(msg.signedData.type)) {
_this.addMessage(msg, { checkIfExists: true });
}
});
}
});
}
});
}, 5000); // TODO: this should be made to work without timeout
}
}
/**
* Use this to load an index that you can write to
* @param {Object} gun gun instance where the index is stored (e.g. new Gun())
* @param {Object} keypair SEA keypair (can be generated with await identifiLib.Key.generate())
* @param {Object} options see default options in Index constructor's example
* @returns {Promise}
*/
Index.create = async function create(gun, keypair) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (!keypair) {
keypair = await _key2.default.getDefault();
}
var user = gun.user();
user.auth(keypair);
options.viewpoint = new _attribute2.default('keyID', _key2.default.getId(keypair));
var i = new Index(user.get('identifi'), options);
i.gun.get('viewpoint').put(options.viewpoint);
var uri = options.viewpoint.uri();
var g = i.gun.get('identitiesBySearchKey').get(uri);
var attrs = {};
attrs[options.viewpoint.uri()] = options.viewpoint;
if (options.self) {
var keys = Object.keys(options.self);
for (var _i = 0; _i < keys.length; _i++) {
var a = new _attribute2.default(keys[_i], options.self[keys[_i]]);
attrs[a.uri()] = a;
}
}
var id = await _identity2.default.create(g, { trustDistance: 0, linkTo: options.viewpoint, attrs: attrs });
await i._addIdentityToIndexes(id.gun);
if (options.self) {
var recipient = Object.assign(options.self, { keyID: options.viewpoint.value });
_message2.default.createVerification({ recipient: recipient }, keypair).then(function (msg) {
i.addMessage(msg);
});
}
return i;
};
Index.getMsgIndexKey = function getMsgIndexKey(msg) {
var distance = parseInt(msg.distance);
distance = Number.isNaN(distance) ? 99 : distance;
distance = ('00' + distance).substring(distance.toString().length); // pad with zeros
var key = distance + ':' + Math.floor(Date.parse(msg.timestamp || msg.signedData.timestamp) / 1000) + ':' + (msg.ipfs_hash || msg.hash).substr(0, 9);
return key;
};
Index.getMsgIndexKeys = function getMsgIndexKeys(msg) {
var keys = {};
var distance = parseInt(msg.distance);
distance = Number.isNaN(distance) ? 99 : distance;
distance = ('00' + distance).substring(distance.toString().length); // pad with zeros
var hashSlice = msg.getHash().substr(0, 9);
keys.messagesByHash = [msg.getHash()];
keys.messagesByTimestamp = [Math.floor(Date.parse(msg.timestamp || msg.signedData.timestamp) / 1000) + ':' + hashSlice];
keys.messagesByDistance = [distance + ':' + keys.messagesByTimestamp[0]];
keys.messagesByAuthor = [];
var authors = msg.getAuthorArray();
for (var i = 0; i < authors.length; i++) {
keys.messagesByAuthor.push(authors[i].uri() + ':' + msg.signedData.timestamp + ':' + hashSlice);
}
keys.messagesByRecipient = [];
var recipients = msg.getRecipientArray();
for (var _i2 = 0; _i2 < recipients.length; _i2++) {
keys.messagesByRecipient.push(recipients[_i2].uri() + ':' + msg.signedData.timestamp + ':' + hashSlice);
}
if (['verification', 'unverification'].indexOf(msg.signedData.type) > -1) {
keys.verificationsByRecipientAndAuthor = [];
for (var _i3 = 0; _i3 < recipients.length; _i3++) {
var r = recipients[_i3];
if (!r.isUniqueType()) {
continue;
}
for (var j = 0; j < authors.length; j++) {
var a = authors[j];
if (!a.isUniqueType()) {
continue;
}
keys.verificationsByRecipientAndAuthor.push(r.uri() + ':' + a.uri());
}
}
} else if (msg.signedData.type === 'rating') {
keys.ratingsByRecipientAndAuthor = [];
for (var _i4 = 0; _i4 < recipients.length; _i4++) {
var _r = recipients[_i4];
if (!_r.isUniqueType()) {
continue;
}
for (var _j = 0; _j < authors.length; _j++) {
var _a = authors[_j];
if (!_a.isUniqueType()) {
continue;
}
keys.ratingsByRecipientAndAuthor.push(_r.uri() + ':' + _a.uri());
}
}
}
return keys;
};
Index.prototype.getIdentityIndexKeys = async function getIdentityIndexKeys(identity, hash) {
var indexKeys = { identitiesByTrustDistance: [], identitiesBySearchKey: [] };
var d = void 0;
if (identity.linkTo && this.viewpoint.equals(identity.linkTo)) {
d = 0;
} else {
d = await _util2.default.timeoutPromise(identity.get('trustDistance').then(), GUN_TIMEOUT);
}
function addIndexKey(a) {
if (!(a && a.value && a.type)) {
// TODO: this sometimes returns undefined
return;
}
var distance = d !== undefined ? d : parseInt(a.dist);
distance = Number.isNaN(distance) ? 99 : distance;
distance = ('00' + distance).substring(distance.toString().length); // pad with zeros
var v = a.value || a[1];
var n = a.type || a[0];
var value = encodeURIComponent(v);
var lowerCaseValue = encodeURIComponent(v.toLowerCase());
var name = encodeURIComponent(n);
var key = value + ':' + name;
var lowerCaseKey = lowerCaseValue + ':' + name;
if (!_attribute2.default.isUniqueType(n)) {
// allow for multiple index keys with same non-unique attribute
key = key + ':' + hash.substr(0, 9);
lowerCaseKey = lowerCaseKey + ':' + hash.substr(0, 9);
}
indexKeys.identitiesBySearchKey.push(key);
indexKeys.identitiesByTrustDistance.push(distance + ':' + key);
if (key !== lowerCaseKey) {
indexKeys.identitiesBySearchKey.push(lowerCaseKey);
indexKeys.identitiesByTrustDistance.push(distance + ':' + lowerCaseKey);
}
if (v.indexOf(' ') > -1) {
var words = v.toLowerCase().split(' ');
for (var l = 0; l < words.length; l += 1) {
var k = encodeURIComponent(words[l]) + ':' + name;
if (!_attribute2.default.isUniqueType(n)) {
k = k + ':' + hash.substr(0, 9);
}
indexKeys.identitiesBySearchKey.push(k);
indexKeys.identitiesByTrustDistance.push(distance + ':' + k);
}
}
if (key.match(/^http(s)?:\/\/.+\/[a-zA-Z0-9_]+$/)) {
var split = key.split('/');
indexKeys.identitiesBySearchKey.push(split[split.length - 1]);
indexKeys.identitiesByTrustDistance.push(distance + ':' + split[split.length - 1]);
}
}
if (this.viewpoint.equals(identity.linkTo)) {
addIndexKey(identity.linkTo);
}
await identity.get('attrs').map().once(addIndexKey).then();
return indexKeys;
};
/**
* @returns {Identity} viewpoint identity of the index
*/
Index.prototype.getViewpoint = async function getViewpoint() {
var vpAttr = void 0;
if (this.viewpoint) {
vpAttr = this.viewpoint;
} else {
vpAttr = new _attribute2.default((await this.gun.get('viewpoint').then()));
}
return new _identity2.default(this.gun.get('identitiesBySearchKey').get(vpAttr.uri()));
};
/**
* Get an identity referenced by an identifier.
* get(type, value)
* get(Attribute)
* get(value) - guesses the type or throws an error
* @returns {Identity} identity that is connected to the identifier param
*/
Index.prototype.get = function get(a, b) {
if (!a) {
throw new Error('get failed: param must be a string, received ' + (typeof a === 'undefined' ? 'undefined' : _typeof(a)) + ' ' + a);
}
var attr = a;
if (a.constructor.name !== 'Attribute') {
var type = void 0,
value = void 0;
if (b) {
type = a;
value = b;
} else {
value = a;
type = _attribute2.default.guessTypeOf(value);
}
attr = new _attribute2.default(type, value);
}
return new _identity2.default(this.gun.get('identitiesBySearchKey').get(attr.uri()), attr);
};
Index.prototype._getMsgs = async function _getMsgs(msgIndex, callback, limit, cursor, desc, filter) {
var results = 0;
async function resultFound(result) {
if (results >= limit) {
return;
}
var msg = await _message2.default.fromSig(result.value);
if (filter && !filter(msg)) {
return;
}
results++;
msg.cursor = result.key;
if (result.value && result.value.ipfsUri) {
msg.ipfsUri = result.value.ipfsUri;
}
callback(msg);
}
searchText(msgIndex, resultFound, '', undefined, cursor, desc);
};
Index.prototype._addIdentityToIndexes = async function _addIdentityToIndexes(id) {
var hash = _gun2.default.node.soul(id) || 'todo';
var indexKeys = await this.getIdentityIndexKeys(id, hash.substr(0, 6));
var indexes = Object.keys(indexKeys);
for (var i = 0; i < indexes.length; i++) {
var index = indexes[i];
for (var j = 0; j < indexKeys[index].length; j++) {
var key = indexKeys[index][j];
console.log('adding key ' + key);
await this.gun.get(index).get(key).put(id);
}
}
};
/**
* Get Messages sent by identity
* @param {Identity} identity identity whose sent Messages to get
* @param {Function} callback callback function that receives the Messages one by one
*/
Index.prototype.getSentMsgs = async function getSentMsgs(identity, callback, limit) {
var cursor = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '';
var filter = arguments[4];
return this._getMsgs(identity.gun.get('sent'), callback, limit, cursor, filter);
};
/**
* Get Messages received by identity
* @param {Identity} identity identity whose received Messages to get
* @param {Function} callback callback function that receives the Messages one by one
*/
Index.prototype.getReceivedMsgs = async function getReceivedMsgs(identity, callback, limit) {
var cursor = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '';
var filter = arguments[4];
return this._getMsgs(identity.gun.get('received'), callback, limit, cursor, filter);
};
Index.prototype._getAttributeTrustDistance = async function _getAttributeTrustDistance(a) {
if (!_attribute2.default.isUniqueType(a.type)) {
return;
}
if (this.viewpoint.equals(a)) {
return 0;
}
var id = this.get(a);
var d = await id.gun.get('trustDistance').then();
if (isNaN(d)) {
d = Infinity;
}
return d;
};
/**
* @param {Message} msg
* @returns {number} trust distance to msg author. Returns undefined if msg signer is not trusted.
*/
Index.prototype.getMsgTrustDistance = async function getMsgTrustDistance(msg) {
var shortestDistance = Infinity;
var signerAttr = new _attribute2.default('keyID', msg.getSignerKeyID());
if (!signerAttr.equals(this.viewpoint)) {
var signer = this.get(signerAttr);
var d = await signer.gun.get('trustDistance').then();
if (isNaN(d)) {
return;
}
}
for (var _iterator = msg.getAuthorArray(), _isArray = Array.isArray(_iterator), _i5 = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _ref;
if (_isArray) {
if (_i5 >= _iterator.length) break;
_ref = _iterator[_i5++];
} else {
_i5 = _iterator.next();
if (_i5.done) break;
_ref = _i5.value;
}
var a = _ref;
var _d = await this._getAttributeTrustDistance(a);
if (_d < shortestDistance) {
shortestDistance = _d;
}
}
return shortestDistance < Infinity ? shortestDistance : undefined;
};
Index.prototype._updateMsgRecipientIdentity = async function _updateMsgRecipientIdentity(msg, msgIndexKey, recipient) {
var hash = 'todo';
var identityIndexKeysBefore = await this.getIdentityIndexKeys(recipient, hash.substr(0, 6));
var attrs = await new Promise(function (resolve) {
recipient.get('attrs').load(function (r) {
return resolve(r);
});
});
if (msg.signedData.type === 'verification') {
var _loop = function _loop() {
if (_isArray2) {
if (_i6 >= _iterator2.length) return 'break';
_ref2 = _iterator2[_i6++];
} else {
_i6 = _iterator2.next();
if (_i6.done) return 'break';
_ref2 = _i6.value;
}
var a = _ref2;
var hasAttr = false;
Object.keys(attrs).forEach(function (k) {
// TODO: if author is self, mark as self verified
if (a.equals(attrs[k])) {
attrs[k].conf = (attrs[k].conf || 0) + 1;
hasAttr = true;
}
});
if (!hasAttr) {
attrs[a.uri()] = { type: a.type, value: a.value, conf: 1, ref: 0 };
}
if (msg.goodVerification) {
attrs[a.uri()].verified = true;
}
};
for (var _iterator2 = msg.getRecipientArray(), _isArray2 = Array.isArray(_iterator2), _i6 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
var _ref2;
var _ret = _loop();
if (_ret === 'break') break;
}
recipient.get('mostVerifiedAttributes').put(_identity2.default.getMostVerifiedAttributes(attrs)); // TODO: why this needs to be done twice to register?
recipient.get('mostVerifiedAttributes').put(_identity2.default.getMostVerifiedAttributes(attrs));
recipient.get('attrs').put(attrs);
recipient.get('attrs').put(attrs);
}
if (msg.signedData.type === 'rating') {
var id = await recipient.then();
id.receivedPositive = id.receivedPositive || 0;
id.receivedNegative = id.receivedNegative || 0;
id.receivedNeutral = id.receivedNeutral || 0;
if (msg.isPositive()) {
if (typeof id.trustDistance !== 'number' || msg.distance + 1 < id.trustDistance) {
recipient.get('trustDistance').put(msg.distance + 1);
}
id.receivedPositive++;
} else {
if (msg.distance < id.trustDistance) {
recipient.get('trustDistance').put(false); // TODO: this should take into account the aggregate score of the identity
}
if (msg.isNegative()) {
id.receivedNegative++;
} else {
id.receivedNeutral++;
}
}
recipient.get('receivedPositive').put(id.receivedPositive);
recipient.get('receivedNegative').put(id.receivedNegative);
recipient.get('receivedNeutral').put(id.receivedNeutral);
if (msg.signedData.context === 'verifier') {
if (msg.distance === 0) {
if (msg.isPositive) {
recipient.get('scores').get(msg.signedData.context).get('score').put(10);
} else if (msg.isNegative()) {
recipient.get('scores').get(msg.signedData.context).get('score').put(0);
} else {
recipient.get('scores').get(msg.signedData.context).get('score').put(-10);
}
}
} else {
// TODO: generic context-dependent score calculation
}
}
var obj = { sig: msg.sig, pubKey: msg.pubKey };
if (msg.ipfsUri) {
obj.ipfsUri = msg.ipfsUri;
}
recipient.get('received').get(msgIndexKey).put(obj);
recipient.get('received').get(msgIndexKey).put(obj);
var identityIndexKeysAfter = await this.getIdentityIndexKeys(recipient, hash.substr(0, 6));
var indexesBefore = Object.keys(identityIndexKeysBefore);
for (var i = 0; i < indexesBefore.length; i++) {
var index = indexesBefore[i];
for (var j = 0; j < identityIndexKeysBefore[index].length; j++) {
var key = identityIndexKeysBefore[index][j];
if (!identityIndexKeysAfter[index] || identityIndexKeysAfter[index].indexOf(key) === -1) {
console.log('removing stale key ' + key + ' from index ' + index);
this.gun.get(index).get(key).put(null);
}
}
}
};
Index.prototype._updateMsgAuthorIdentity = async function _updateMsgAuthorIdentity(msg, msgIndexKey, author) {
if (msg.signedData.type === 'rating') {
var id = await author.then();
id.sentPositive = id.sentPositive || 0;
id.sentNegative = id.sentNegative || 0;
id.sentNeutral = id.sentNeutral || 0;
if (msg.isPositive()) {
id.sentPositive++;
} else if (msg.isNegative()) {
id.sentNegative++;
} else {
id.sentNeutral++;
}
author.get('sentPositive').put(id.sentPositive);
author.get('sentNegative').put(id.sentNegative);
author.get('sentNeutral').put(id.sentNeutral);
}
var obj = { sig: msg.sig, pubKey: msg.pubKey };
if (msg.ipfsUri) {
obj.ipfsUri = msg.ipfsUri;
}
author.get('sent').get(msgIndexKey).put(obj); // for some reason, doesn't work unless I do it twice
author.get('sent').get(msgIndexKey).put(obj);
return;
};
Index.prototype._updateIdentityProfilesByMsg = async function _updateIdentityProfilesByMsg(msg, authorIdentities, recipientIdentities) {
var msgIndexKey = Index.getMsgIndexKey(msg);
msgIndexKey = msgIndexKey.substr(msgIndexKey.indexOf(':') + 1);
var ids = Object.values(Object.assign({}, authorIdentities, recipientIdentities));
for (var i = 0; i < ids.length; i++) {
// add new identifiers to identity
var data = await ids[i].gun.then(); // TODO: data is sometimes undefined and new identity is not added!
var relocated = data ? this.gun.get('identities').set(data) : ids[i].gun; // this may screw up real time updates? and create unnecessary `identities` entries
if (recipientIdentities.hasOwnProperty(ids[i].gun['_'].link)) {
await this._updateMsgRecipientIdentity(msg, msgIndexKey, ids[i].gun);
}
if (authorIdentities.hasOwnProperty(ids[i].gun['_'].link)) {
await this._updateMsgAuthorIdentity(msg, msgIndexKey, ids[i].gun);
}
await this._addIdentityToIndexes(relocated);
}
};
Index.prototype.removeTrustedIndex = async function removeTrustedIndex(gunUri) {
this.gun.get('trustedIndexes').get(gunUri).put(null);
};
Index.prototype.addTrustedIndex = async function addTrustedIndex(gunUri) {
var _this2 = this;
var maxMsgsToCrawl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.options.indexSync.importOnAdd.maxMsgCount;
var maxMsgDistance = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.options.indexSync.importOnAdd.maxMsgDistance;
if (gunUri === this.viewpoint.value) {
return;
}
console.log('addTrustedIndex', gunUri);
var exists = await this.gun.get('trustedIndexes').get(gunUri).then();
if (exists) {
return;
}
this.gun.get('trustedIndexes').get(gunUri).put(true);
var msgs = [];
if (this.options.indexSync.importOnAdd.enabled) {
await _util2.default.timeoutPromise(new Promise(function (resolve) {
_this2.gun.user(gunUri).get('identifi').get('messagesByDistance').map(function (val, key) {
var d = Number.parseInt(key.split(':')[0]);
if (!isNaN(d) && d <= maxMsgDistance) {
_message2.default.fromSig(val).then(function (msg) {
msgs.push(msg);
if (msgs.length >= maxMsgsToCrawl) {
resolve();
}
});
}
});
}), 10000);
console.log('adding', msgs.length, 'msgs');
this.addMessages(msgs);
}
};
Index.prototype._updateIdentityIndexesByMsg = async function _updateIdentityIndexesByMsg(msg) {
var recipientIdentities = {};
var authorIdentities = {};
var selfAuthored = false;
for (var _iterator3 = msg.getAuthorArray(), _isArray3 = Array.isArray(_iterator3), _i7 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) {
var _ref3;
if (_isArray3) {
if (_i7 >= _iterator3.length) break;
_ref3 = _iterator3[_i7++];
} else {
_i7 = _iterator3.next();
if (_i7.done) break;
_ref3 = _i7.value;
}
var _a3 = _ref3;
var _id = this.get(_a3);
var td = await _util2.default.timeoutPromise(_id.gun.get('trustDistance').then(), GUN_TIMEOUT);
if (!isNaN(td)) {
authorIdentities[_id.gun['_'].link] = _id;
var scores = await _id.gun.get('scores').then();
if (scores && scores.verifier && msg.signedData.type === 'verification') {
msg.goodVerification = true;
}
if (td === 0) {
selfAuthored = true;
}
}
}
if (!Object.keys(authorIdentities).length) {
return; // unknown author, do nothing
}
for (var _iterator4 = msg.getRecipientArray(), _isArray4 = Array.isArray(_iterator4), _i8 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) {
var _ref4;
if (_isArray4) {
if (_i8 >= _iterator4.length) break;
_ref4 = _iterator4[_i8++];
} else {
_i8 = _iterator4.next();
if (_i8.done) break;
_ref4 = _i8.value;
}
var _a4 = _ref4;
var _id2 = this.get(_a4);
var _td = await _util2.default.timeoutPromise(_id2.gun.get('trustDistance').then(), GUN_TIMEOUT);
if (!isNaN(_td)) {
recipientIdentities[_id2.gun['_'].link] = _id2;
}
if (selfAuthored && _a4.type === 'keyID' && _a4.value !== this.viewpoint.value) {
// TODO: not if already added - causes infinite loop?
if (msg.isPositive()) {
this.addTrustedIndex(_a4.value);
} else {
this.removeTrustedIndex(_a4.value);
}
}
}
if (!Object.keys(recipientIdentities).length) {
// recipient is previously unknown
var attrs = {};
for (var _iterator5 = msg.getRecipientArray(), _isArray5 = Array.isArray(_iterator5), _i9 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) {
var _ref5;
if (_isArray5) {
if (_i9 >= _iterator5.length) break;
_ref5 = _iterator5[_i9++];
} else {
_i9 = _iterator5.next();
if (_i9.done) break;
_ref5 = _i9.value;
}
var _a2 = _ref5;
attrs[_a2.uri()] = _a2;
}
var linkTo = _identity2.default.getLinkTo(attrs);
var random = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); // TODO: bubblegum fix
var trustDistance = msg.isPositive() && typeof msg.distance === 'number' ? msg.distance + 1 : false;
var id = await _identity2.default.create(this.gun.get('identities').get(random).put({}), { attrs: attrs, linkTo: linkTo, trustDistance: trustDistance });
// {a:1} because inserting {} causes a "no signature on data" error from gun
// TODO: take msg author trust into account
recipientIdentities[id.gun['_'].link] = id;
}
return this._updateIdentityProfilesByMsg(msg, authorIdentities, recipientIdentities);
};
/**
* Add a list of messages to the index.
* Useful for example when adding a new WoT dataset that contains previously
* unknown authors.
*
* Iteratively performs sorted merge joins on [previously known identities] and
* [new msgs authors], until all messages from within the WoT have been added.
*
* @param {Array} msgs an array of messages.
* @param {Object} ipfs (optional) ipfs instance where the messages are saved
* @returns {boolean} true on success
*/
Index.prototype.addMessages = async function addMessages(msgs) {
var _this3 = this;
var msgsByAuthor = {};
if (Array.isArray(msgs)) {
console.log('sorting ' + msgs.length + ' messages onto a search tree...');
for (var i = 0; i < msgs.length; i++) {
for (var _iterator6 = msgs[i].getAuthorArray(), _isArray6 = Array.isArray(_iterator6), _i10 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) {
var _ref6;
if (_isArray6) {
if (_i10 >= _iterator6.length) break;
_ref6 = _iterator6[_i10++];
} else {
_i10 = _iterator6.next();
if (_i10.done) break;
_ref6 = _i10.value;
}
var _a5 = _ref6;
if (_a5.isUniqueType()) {
var key = _a5.uri() + ':' + msgs[i].getHash();
msgsByAuthor[key] = msgs[i];
}
}
}
console.log('...done');
} else {
throw 'msgs param must be an array';
}
var msgAuthors = Object.keys(msgsByAuthor).sort();
if (!msgAuthors.length) {
return;
}
var initialMsgCount = void 0,
msgCountAfterwards = void 0;
var index = this.gun.get('identitiesBySearchKey');
var _loop2 = async function _loop2() {
var knownIdentities = [];
var stop = false;
searchText(index, function (result) {
if (stop) {
return;
}
knownIdentities.push(result);
}, '');
await new Promise(function (r) {
return setTimeout(r, 2000);
}); // wait for results to accumulate
stop = true;
knownIdentities.sort(function (a, b) {
if (a.key === b.key) {
return 0;
} else if (a.key > b.key) {
return 1;
} else {
return -1;
}
});
var i = 0;
var author = msgAuthors[i];
var knownIdentity = knownIdentities.shift();
initialMsgCount = msgAuthors.length;
// sort-merge join identitiesBySearchKey and msgsByAuthor
while (author && knownIdentity) {
if (author.indexOf(knownIdentity.key) === 0) {
try {
await _util2.default.timeoutPromise(_this3.addMessage(msgsByAuthor[author], { checkIfExists: true }), 10000);
} catch (e) {
console.log('adding failed:', e, JSON.stringify(msgsByAuthor[author], null, 2));
}
msgAuthors.splice(i, 1);
author = i < msgAuthors.length ? msgAuthors[i] : undefined;
//knownIdentity = knownIdentities.shift();
} else if (author < knownIdentity.key) {
author = i < msgAuthors.length ? msgAuthors[++i] : undefined;
} else {
knownIdentity = knownIdentities.shift();
}
}
msgCountAfterwards = msgAuthors.length;
};
do {
await _loop2();
} while (msgCountAfterwards !== initialMsgCount);
return true;
};
/**
* @param msg Message to add to the index
* @param ipfs (optional) ipfs instance where the message is additionally saved
*/
Index.prototype.addMessage = async function addMessage(msg) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (msg.constructor.name !== 'Message') {
throw new Error('addMessage failed: param must be a Message, received ' + msg.constructor.name);
}
var hash = msg.getHash();
if (true === options.checkIfExists) {
var exists = await this.gun.get('messagesByHash').get(hash).once().then();
if (exists) {
return;
}
}
msg.distance = await this.getMsgTrustDistance(msg);
if (msg.distance === undefined) {
return false; // do not save messages from untrusted author
}
var obj = { sig: msg.sig, pubKey: msg.pubKey };
var indexKeys = Index.getMsgIndexKeys(msg);
for (var index in indexKeys) {
for (var i = 0; i < indexKeys[index].length; i++) {
var key = indexKeys[index][i];
console.log('adding to index ' + index + ' message key ' + key);
this.gun.get(index).get(key).put(obj);
this.gun.get(index).get(key).put(obj); // umm, what? doesn't work unless I write it twice
}
}
if (this.options.ipfs) {
try {
var ipfsUri = await msg.saveToIpfs(this.options.ipfs);
obj.ipfsUri = ipfsUri;
this.gun.get('messagesByHash').get(ipfsUri).put(obj);
this.gun.get('messagesByHash').get(ipfsUri).put(obj);
} catch (e) {
console.error('adding msg ' + msg + ' to ipfs failed: ' + e);
}
}
await this._updateIdentityIndexesByMsg(msg);
return true;
};
/**
* @param {string} value search string
* @param {string} type (optional) type of searched value
* @returns {Array} list of matching identities
*/
Index.prototype.search = async function search(value, type, callback, limit) {
var _this4 = this;
// TODO: param 'exact', type param
var seen = {};
function searchTermCheck(key) {
var arr = key.split(':');
if (arr.length < 3) {
return false;
}
var keyValue = arr[1];
var keyType = arr[2];
if (keyValue.indexOf(encodeURIComponent(value)) !== 0) {
return false;
}
if (type && keyType !== type) {
return false;
}
return true;
}
this.gun.get('identitiesByTrustDistance').map().once(function (id, key) {
if (Object.keys(seen).length >= limit) {
// TODO: turn off .map cb
return;
}
if (!searchTermCheck(key)) {
return;
}
var soul = _gun2.default.node.soul(id);
if (soul && !seen.hasOwnProperty(soul)) {
seen[soul] = true;
var identity = new _identity2.default(_this4.gun.get('identitiesByTrustDistance').get(key));
identity.cursor = key;
callback(identity);
}
});
if (this.options.indexSync.query.enabled) {
this.gun.get('trustedIndexes').map().once(function (val, key) {
if (val) {
_this4.gun.user(key).get('identifi').get('identitiesByTrustDistance').map().once(function (id, k) {
if (Object.keys(seen).length >= limit) {
// TODO: turn off .map cb
return;
}
if (!searchTermCheck(key)) {
return;
}
var soul = _gun2.default.node.soul(id);
if (soul && !seen.hasOwnProperty(soul)) {
seen[soul] = true;
callback(new _identity2.default(_this4.gun.user(key).get('identifi').get('identitiesByTrustDistance').get(k)));
}
});
}
});
}
};
/**
* @returns {Array} list of messages
*/
Index.prototype.getMessagesByTimestamp = function getMessagesByTimestamp(callback, limit) {
var cursor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
var _this5 = this;
var desc = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var filter = arguments[4];
var seen = {};
var cb = function cb(msg) {
console.log('hash', msg.hash);
if ((!limit || Object.keys(seen).length <= limit) && !seen.hasOwnProperty(msg.hash)) {
seen[msg.hash] = true;
callback(msg);
}
};
this._getMsgs(this.gun.get('messagesByTimestamp'), cb, limit, cursor, filter);
if (this.options.indexSync.query.enabled) {
this.gun.get('trustedIndexes').map().once(function (val, key) {
if (val) {
var n = _this5.gun.user(key).get('identifi').get('messagesByTimestamp');
_this5._getMsgs(n, cb, limit, cursor, desc, filter);
}
});
}
};
/**
* @returns {Array} list of messages
*/
Index.prototype.getMessagesByDistance = function getMessagesByDistance(callback, limit) {
var cursor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
var _this6 = this;
var desc = arguments[3];
var filter = arguments[4];
var seen = {};
var cb = function cb(msg) {
if (!seen.hasOwnProperty(msg.hash)) {
if ((!limit || Object.keys(seen).length <= limit) && !seen.hasOwnProperty(msg.hash)) {
seen[msg.hash] = true;
callback(msg);
}
}
};
this._getMsgs(this.gun.get('messagesByDistance'), cb, limit, cursor, desc, filter);
if (this.options.indexSync.query.enabled) {
this.gun.get('trustedIndexes').map().once(function (val, key) {
if (val) {
var n = _this6.gun.user(key).get('identifi').get('messagesByDistance');
_this6._getMsgs(n, cb, limit, cursor, desc, filter);
}
});
}
};
return Index;
}();
exports.default = Index;
module.exports = exports['default'];