@lyra/document-store
Version:
Lyra / saga document store
225 lines (183 loc) • 7.32 kB
JavaScript
;
var _omit2 = require('lodash/omit');
var _omit3 = _interopRequireDefault(_omit2);
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _rxjs = require('rxjs');
var _operators = require('rxjs/operators');
var _nanoPubsub = require('nano-pubsub');
var _nanoPubsub2 = _interopRequireDefault(_nanoPubsub);
var _mutator = require('@lyra/mutator');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const NOOP = () => {};
function createBufferedDocument(documentId, serverEvents$, doCommit) {
const reconnects$ = serverEvents$.pipe((0, _operators.filter)(event => event.type === 'reconnect'));
const saves = (0, _nanoPubsub2.default)();
const bufferedDocs$ = serverEvents$.pipe((0, _operators.filter)(event => event.type === 'snapshot'), (0, _operators.map)(event => event.document), (0, _operators.map)(snapshot => {
const bufferedDocument = new _mutator.BufferedDocument(snapshot || null);
bufferedDocument.commitHandler = function commitHandler(opts) {
const payload = opts.mutation.params;
// TODO:
// right now the BufferedDocument just commits fire-and-forget-ish
// We should be able to handle failures and retry here
doCommit((0, _omit3.default)(payload, 'resultRev')).subscribe({
next: res => {
opts.success(res);
saves.publish();
},
error: opts.failure
});
};
const rebase$ = new _rxjs.Observable(rebaseObserver => {
bufferedDocument.onRebase = edge => {
rebaseObserver.next({ type: 'rebase', document: edge });
};
return () => {
bufferedDocument.onRebase = NOOP;
};
}).pipe((0, _operators.share)());
const mutation$ = new _rxjs.Observable(mutationObserver => {
bufferedDocument.onMutation = ({ mutation, remote }) => {
mutationObserver.next({
type: 'mutation',
document: bufferedDocument.LOCAL,
mutations: mutation.mutations,
origin: remote ? 'remote' : 'local'
});
};
const serverMutations = serverEvents$.pipe((0, _operators.filter)(event => event.type === 'mutation')).subscribe(event => bufferedDocument.arrive(new _mutator.Mutation(event)));
return () => {
serverMutations.unsubscribe();
bufferedDocument.onMutation = NOOP;
};
}).pipe((0, _operators.share)());
return {
events: new _rxjs.Observable(observer => {
observer.next({ type: 'snapshot', document: bufferedDocument.LOCAL });
observer.complete();
}).pipe((0, _operators.concat)((0, _rxjs.merge)(mutation$, rebase$, reconnects$))),
patch(patches) {
const mutations = patches.map(patch => ({
patch: _extends({}, patch, { id: documentId })
}));
bufferedDocument.add(new _mutator.Mutation({ mutations: mutations }));
},
create(document) {
const mutation = {
create: Object.assign({ id: documentId }, document)
};
bufferedDocument.add(new _mutator.Mutation({ mutations: [mutation] }));
},
createIfNotExists(document) {
bufferedDocument.add(new _mutator.Mutation({ mutations: [{ createIfNotExists: document }] }));
},
createOrReplace(document) {
bufferedDocument.add(new _mutator.Mutation({ mutations: [{ createOrReplace: document }] }));
},
delete() {
bufferedDocument.add(new _mutator.Mutation({ mutations: [{ delete: { id: documentId } }] }));
},
commit() {
return new _rxjs.Observable(observer => {
// todo: connect observable with request from bufferedDocument.commit somehow
bufferedDocument.commit();
return saves.subscribe(() => {
observer.next();
observer.complete();
});
});
}
};
}), (0, _operators.share)());
let currentBuffered;
const cachedBuffered = new _rxjs.Observable(observer => {
if (currentBuffered) {
observer.next(currentBuffered);
observer.complete();
}
return bufferedDocs$.pipe((0, _operators.tap)(doc => {
currentBuffered = doc;
})).subscribe(observer);
});
return {
events: cachedBuffered.pipe((0, _operators.switchMap)(bufferedDoc => bufferedDoc.events)),
patch(patches) {
cachedBuffered.subscribe(bufferedDoc => bufferedDoc.patch(patches));
},
create(document) {
cachedBuffered.subscribe(bufferedDoc => bufferedDoc.create(document));
},
createIfNotExists(document) {
cachedBuffered.subscribe(bufferedDoc => bufferedDoc.createIfNotExists(document));
},
createOrReplace(document) {
cachedBuffered.subscribe(bufferedDoc => bufferedDoc.createOrReplace(document));
},
delete() {
cachedBuffered.subscribe(bufferedDoc => bufferedDoc.delete());
},
commit() {
return cachedBuffered.pipe((0, _operators.switchMap)(bufferedDoc => bufferedDoc.commit()));
}
};
}
const isDocId = id => event => event.documentId === id;
module.exports = function createDocumentStore({ serverConnection }) {
return {
byId,
byIds,
query,
create,
checkout,
checkoutPair,
patch: patchDoc,
delete: deleteDoc,
createOrReplace: createOrReplace,
createIfNotExists: createIfNotExists
};
function patchDoc(documentId, patches) {
const doc = checkout(documentId);
doc.patch(patches);
return doc.commit();
}
function deleteDoc(documentId) {
return checkout(documentId).delete().commit();
}
function byId(documentId) {
return checkout(documentId).events;
}
function checkoutPair(idPair) {
const publishedId = idPair.publishedId,
draftId = idPair.draftId;
const serverEvents$ = serverConnection.byIdPair({ publishedId, draftId }).pipe((0, _operators.share)());
const draft = createBufferedDocument(draftId, serverEvents$.pipe((0, _operators.filter)(isDocId(draftId))), doCommit);
const published = createBufferedDocument(publishedId, serverEvents$.pipe((0, _operators.filter)(isDocId(publishedId))), doCommit);
return { draft, published };
}
function checkout(documentId) {
const serverEvents$ = serverConnection.byId(documentId).pipe((0, _operators.share)());
return createBufferedDocument(documentId, serverEvents$, doCommit);
}
function byIds(documentIds) {
return new _rxjs.Observable(observer => {
const documentSubscriptions = documentIds.map(id => byId(id).subscribe(observer));
return () => {
documentSubscriptions.map(subscription => subscription.unsubscribe());
};
});
}
function query(_query, params) {
return serverConnection.query(_query, params);
}
function create(document) {
return serverConnection.create(document);
}
function createIfNotExists(document) {
return serverConnection.createIfNotExists(document);
}
function createOrReplace(document) {
return serverConnection.createOrReplace(document);
}
function doCommit(payload) {
return serverConnection.mutate(payload);
}
};