UNPKG

@lyra/document-store

Version:

Lyra / saga document store

225 lines (183 loc) 7.32 kB
'use strict'; 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); } };