rxdb
Version:
A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/
260 lines (253 loc) • 9.74 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {
replicateWebRTC: true,
RxWebRTCReplicationPool: true
};
exports.RxWebRTCReplicationPool = void 0;
exports.replicateWebRTC = replicateWebRTC;
var _rxjs = require("rxjs");
var _plugin = require("../../plugin.js");
var _index = require("../../replication-protocol/index.js");
var _index2 = require("../../plugins/utils/index.js");
var _index3 = require("../leader-election/index.js");
var _index4 = require("../replication/index.js");
var _webrtcHelper = require("./webrtc-helper.js");
Object.keys(_webrtcHelper).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _webrtcHelper[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _webrtcHelper[key];
}
});
});
var _rxError = require("../../rx-error.js");
var _signalingServer = require("./signaling-server.js");
Object.keys(_signalingServer).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _signalingServer[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _signalingServer[key];
}
});
});
var _webrtcTypes = require("./webrtc-types.js");
Object.keys(_webrtcTypes).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _webrtcTypes[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _webrtcTypes[key];
}
});
});
var _connectionHandlerSimplePeer = require("./connection-handler-simple-peer.js");
Object.keys(_connectionHandlerSimplePeer).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _connectionHandlerSimplePeer[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _connectionHandlerSimplePeer[key];
}
});
});
async function replicateWebRTC(options) {
var collection = options.collection;
(0, _plugin.addRxPlugin)(_index3.RxDBLeaderElectionPlugin);
// fill defaults
if (options.pull) {
if (!options.pull.batchSize) {
options.pull.batchSize = 20;
}
}
if (options.push) {
if (!options.push.batchSize) {
options.push.batchSize = 20;
}
}
if (collection.database.multiInstance) {
await collection.database.waitForLeadership();
}
// used to easier debug stuff
var requestCounter = 0;
var requestFlag = (0, _index2.randomToken)(10);
function getRequestId() {
var count = requestCounter++;
return collection.database.token + '|' + requestFlag + '|' + count;
}
var storageToken = await collection.database.storageToken;
var pool = new RxWebRTCReplicationPool(collection, options, await options.connectionHandlerCreator(options));
pool.subs.push(pool.connectionHandler.error$.subscribe(err => pool.error$.next(err)), pool.connectionHandler.disconnect$.subscribe(peer => pool.removePeer(peer)));
/**
* Answer if someone requests our storage token
*/
pool.subs.push(pool.connectionHandler.message$.pipe((0, _rxjs.filter)(data => data.message.method === 'token')).subscribe(data => {
pool.connectionHandler.send(data.peer, {
id: data.message.id,
result: storageToken
});
}));
var connectSub = pool.connectionHandler.connect$.pipe((0, _rxjs.filter)(() => !pool.canceled)).subscribe(async peer => {
if (options.isPeerValid) {
var isValid = await options.isPeerValid(peer);
if (!isValid) {
return;
}
}
var peerToken;
try {
var tokenResponse = await (0, _webrtcHelper.sendMessageAndAwaitAnswer)(pool.connectionHandler, peer, {
id: getRequestId(),
method: 'token',
params: []
});
peerToken = tokenResponse.result;
} catch (error) {
/**
* If could not get the tokenResponse,
* just ignore that peer.
*/
pool.error$.next((0, _rxError.newRxError)('RC_WEBRTC_PEER', {
error
}));
return;
}
var isMaster = await (0, _webrtcHelper.isMasterInWebRTCReplication)(collection.database.hashFunction, storageToken, peerToken);
var replicationState;
if (isMaster) {
var masterHandler = pool.masterReplicationHandler;
var masterChangeStreamSub = masterHandler.masterChangeStream$.subscribe(ev => {
var streamResponse = {
id: 'masterChangeStream$',
result: ev
};
pool.connectionHandler.send(peer, streamResponse);
});
// clean up the subscription
pool.subs.push(masterChangeStreamSub, pool.connectionHandler.disconnect$.pipe((0, _rxjs.filter)(p => p === peer)).subscribe(() => masterChangeStreamSub.unsubscribe()));
var messageSub = pool.connectionHandler.message$.pipe((0, _rxjs.filter)(data => data.peer === peer), (0, _rxjs.filter)(data => data.message.method !== 'token')).subscribe(async data => {
var {
peer: msgPeer,
message
} = data;
/**
* If it is not a function,
* it means that the client requested the masterChangeStream$
*/
var method = masterHandler[message.method].bind(masterHandler);
var result = await method(...message.params);
var response = {
id: message.id,
result
};
pool.connectionHandler.send(msgPeer, response);
});
pool.subs.push(messageSub);
} else {
replicationState = (0, _index4.replicateRxCollection)({
replicationIdentifier: [collection.name, options.topic, peerToken].join('||'),
collection: collection,
autoStart: true,
deletedField: '_deleted',
live: true,
retryTime: options.retryTime,
waitForLeadership: false,
pull: options.pull ? Object.assign({}, options.pull, {
async handler(lastPulledCheckpoint) {
var answer = await (0, _webrtcHelper.sendMessageAndAwaitAnswer)(pool.connectionHandler, peer, {
method: 'masterChangesSince',
params: [lastPulledCheckpoint, (0, _index2.ensureNotFalsy)(options.pull).batchSize],
id: getRequestId()
});
return answer.result;
},
stream$: pool.connectionHandler.response$.pipe((0, _rxjs.filter)(m => m.response.id === 'masterChangeStream$'), (0, _rxjs.map)(m => m.response.result))
}) : undefined,
push: options.push ? Object.assign({}, options.push, {
async handler(docs) {
var answer = await (0, _webrtcHelper.sendMessageAndAwaitAnswer)(pool.connectionHandler, peer, {
method: 'masterWrite',
params: [docs],
id: getRequestId()
});
return answer.result;
}
}) : undefined
});
}
pool.addPeer(peer, replicationState);
});
pool.subs.push(connectSub);
return pool;
}
/**
* Because the WebRTC replication runs between many instances,
* we use a Pool instead of returning a single replication state.
*/
var RxWebRTCReplicationPool = exports.RxWebRTCReplicationPool = /*#__PURE__*/function () {
function RxWebRTCReplicationPool(collection, options, connectionHandler) {
this.peerStates$ = new _rxjs.BehaviorSubject(new Map());
this.canceled = false;
this.subs = [];
this.error$ = new _rxjs.Subject();
this.collection = collection;
this.options = options;
this.connectionHandler = connectionHandler;
this.collection.onClose.push(() => this.cancel());
this.masterReplicationHandler = (0, _index.rxStorageInstanceToReplicationHandler)(collection.storageInstance, collection.conflictHandler, collection.database.token);
}
var _proto = RxWebRTCReplicationPool.prototype;
_proto.addPeer = function addPeer(peer,
// only if isMaster=false it has a replicationState
replicationState) {
var peerState = {
peer,
replicationState,
subs: []
};
this.peerStates$.next(this.peerStates$.getValue().set(peer, peerState));
if (replicationState) {
peerState.subs.push(replicationState.error$.subscribe(ev => this.error$.next(ev)));
}
};
_proto.removePeer = function removePeer(peer) {
var peerState = (0, _index2.getFromMapOrThrow)(this.peerStates$.getValue(), peer);
this.peerStates$.getValue().delete(peer);
this.peerStates$.next(this.peerStates$.getValue());
peerState.subs.forEach(sub => sub.unsubscribe());
if (peerState.replicationState) {
peerState.replicationState.cancel();
}
}
// often used in unit tests
;
_proto.awaitFirstPeer = function awaitFirstPeer() {
return (0, _rxjs.firstValueFrom)(this.peerStates$.pipe((0, _rxjs.filter)(peerStates => peerStates.size > 0)));
};
_proto.cancel = async function cancel() {
if (this.canceled) {
return;
}
this.canceled = true;
this.subs.forEach(sub => sub.unsubscribe());
Array.from(this.peerStates$.getValue().keys()).forEach(peer => {
this.removePeer(peer);
});
await this.connectionHandler.close();
};
return RxWebRTCReplicationPool;
}(); // export * from './connection-handler-webtorrent';
// export * from './connection-handler-p2pcf';
//# sourceMappingURL=index.js.map
;