UNPKG

test-rxdb

Version:

A local realtime NoSQL Database for JavaScript applications -

398 lines (391 loc) 14.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.basePrototype = void 0; exports.beforeDocumentUpdateWrite = beforeDocumentUpdateWrite; exports.createRxDocumentConstructor = createRxDocumentConstructor; exports.createWithConstructor = createWithConstructor; exports.isRxDocument = isRxDocument; var _operators = require("rxjs/operators"); var _index = require("./plugins/utils/index.js"); var _rxError = require("./rx-error.js"); var _hooks = require("./hooks.js"); var _rxChangeEvent = require("./rx-change-event.js"); var _overwritable = require("./overwritable.js"); var _rxSchemaHelper = require("./rx-schema-helper.js"); var _rxStorageHelper = require("./rx-storage-helper.js"); var _incrementalWrite = require("./incremental-write.js"); var basePrototype = exports.basePrototype = { get primaryPath() { var _this = this; if (!_this.isInstanceOfRxDocument) { return undefined; } return _this.collection.schema.primaryPath; }, get primary() { var _this = this; if (!_this.isInstanceOfRxDocument) { return undefined; } return _this._data[_this.primaryPath]; }, get revision() { var _this = this; if (!_this.isInstanceOfRxDocument) { return undefined; } return _this._data._rev; }, get deleted$() { var _this = this; if (!_this.isInstanceOfRxDocument) { return undefined; } return _this.$.pipe((0, _operators.map)(d => d._data._deleted)); }, get deleted$$() { var _this = this; var reactivity = _this.collection.database.getReactivityFactory(); return reactivity.fromObservable(_this.deleted$, _this.getLatest().deleted, _this.collection.database); }, get deleted() { var _this = this; if (!_this.isInstanceOfRxDocument) { return undefined; } return _this._data._deleted; }, getLatest() { var latestDocData = this.collection._docCache.getLatestDocumentData(this.primary); return this.collection._docCache.getCachedRxDocument(latestDocData); }, /** * returns the observable which emits the plain-data of this document */ get $() { var _this = this; return _this.collection.$.pipe((0, _operators.filter)(changeEvent => !changeEvent.isLocal), (0, _operators.filter)(changeEvent => changeEvent.documentId === this.primary), (0, _operators.map)(changeEvent => (0, _rxChangeEvent.getDocumentDataOfRxChangeEvent)(changeEvent)), (0, _operators.startWith)(_this.collection._docCache.getLatestDocumentData(this.primary)), (0, _operators.distinctUntilChanged)((prev, curr) => prev._rev === curr._rev), (0, _operators.map)(docData => this.collection._docCache.getCachedRxDocument(docData)), (0, _operators.shareReplay)(_index.RXJS_SHARE_REPLAY_DEFAULTS)); }, get $$() { var _this = this; var reactivity = _this.collection.database.getReactivityFactory(); return reactivity.fromObservable(_this.$, _this.getLatest()._data, _this.collection.database); }, /** * returns observable of the value of the given path */ get$(path) { if (_overwritable.overwritable.isDevMode()) { if (path.includes('.item.')) { throw (0, _rxError.newRxError)('DOC1', { path }); } if (path === this.primaryPath) { throw (0, _rxError.newRxError)('DOC2'); } // final fields cannot be modified and so also not observed if (this.collection.schema.finalFields.includes(path)) { throw (0, _rxError.newRxError)('DOC3', { path }); } var schemaObj = (0, _rxSchemaHelper.getSchemaByObjectPath)(this.collection.schema.jsonSchema, path); if (!schemaObj) { throw (0, _rxError.newRxError)('DOC4', { path }); } } return this.$.pipe((0, _operators.map)(data => (0, _index.getProperty)(data, path)), (0, _operators.distinctUntilChanged)()); }, get$$(path) { var obs = this.get$(path); var reactivity = this.collection.database.getReactivityFactory(); return reactivity.fromObservable(obs, this.getLatest().get(path), this.collection.database); }, /** * populate the given path */ populate(path) { var schemaObj = (0, _rxSchemaHelper.getSchemaByObjectPath)(this.collection.schema.jsonSchema, path); var value = this.get(path); if (!value) { return _index.PROMISE_RESOLVE_NULL; } if (!schemaObj) { throw (0, _rxError.newRxError)('DOC5', { path }); } if (!schemaObj.ref) { throw (0, _rxError.newRxError)('DOC6', { path, schemaObj }); } var refCollection = this.collection.database.collections[schemaObj.ref]; if (!refCollection) { throw (0, _rxError.newRxError)('DOC7', { ref: schemaObj.ref, path, schemaObj }); } if (schemaObj.type === 'array') { return refCollection.findByIds(value).exec().then(res => { var valuesIterator = res.values(); return Array.from(valuesIterator); }); } else { return refCollection.findOne(value).exec(); } }, /** * get data by objectPath * @hotPath Performance here is really important, * run some tests before changing anything. */ get(objPath) { return getDocumentProperty(this, objPath); }, toJSON(withMetaFields = false) { if (!withMetaFields) { var data = (0, _index.flatClone)(this._data); delete data._rev; delete data._attachments; delete data._deleted; delete data._meta; return _overwritable.overwritable.deepFreezeWhenDevMode(data); } else { return _overwritable.overwritable.deepFreezeWhenDevMode(this._data); } }, toMutableJSON(withMetaFields = false) { return (0, _index.clone)(this.toJSON(withMetaFields)); }, /** * updates document * @overwritten by plugin (optional) * @param updateObj mongodb-like syntax */ update(_updateObj) { throw (0, _index.pluginMissing)('update'); }, incrementalUpdate(_updateObj) { throw (0, _index.pluginMissing)('update'); }, updateCRDT(_updateObj) { throw (0, _index.pluginMissing)('crdt'); }, putAttachment() { throw (0, _index.pluginMissing)('attachments'); }, getAttachment() { throw (0, _index.pluginMissing)('attachments'); }, allAttachments() { throw (0, _index.pluginMissing)('attachments'); }, get allAttachments$() { throw (0, _index.pluginMissing)('attachments'); }, async modify(mutationFunction, // used by some plugins that wrap the method _context) { var oldData = this._data; var newData = await (0, _incrementalWrite.modifierFromPublicToInternal)(mutationFunction)(oldData); return this._saveData(newData, oldData); }, /** * runs an incremental update over the document * @param function that takes the document-data and returns a new data-object */ incrementalModify(mutationFunction, // used by some plugins that wrap the method _context) { return this.collection.incrementalWriteQueue.addWrite(this._data, (0, _incrementalWrite.modifierFromPublicToInternal)(mutationFunction)).then(result => this.collection._docCache.getCachedRxDocument(result)); }, patch(patch) { var oldData = this._data; var newData = (0, _index.clone)(oldData); Object.entries(patch).forEach(([k, v]) => { newData[k] = v; }); return this._saveData(newData, oldData); }, /** * patches the given properties */ incrementalPatch(patch) { return this.incrementalModify(docData => { Object.entries(patch).forEach(([k, v]) => { docData[k] = v; }); return docData; }); }, /** * saves the new document-data * and handles the events */ async _saveData(newData, oldData) { newData = (0, _index.flatClone)(newData); // deleted documents cannot be changed if (this._data._deleted) { throw (0, _rxError.newRxError)('DOC11', { id: this.primary, document: this }); } await beforeDocumentUpdateWrite(this.collection, newData, oldData); var writeRows = [{ previous: oldData, document: newData }]; var writeResult = await this.collection.storageInstance.bulkWrite(writeRows, 'rx-document-save-data'); var isError = writeResult.error[0]; (0, _rxStorageHelper.throwIfIsStorageWriteError)(this.collection, this.primary, newData, isError); await this.collection._runHooks('post', 'save', newData, this); return this.collection._docCache.getCachedRxDocument((0, _rxStorageHelper.getWrittenDocumentsFromBulkWriteResponse)(this.collection.schema.primaryPath, writeRows, writeResult)[0]); }, /** * Remove the document. * Notice that there is no hard delete, * instead deleted documents get flagged with _deleted=true. */ remove() { var collection = this.collection; if (this.deleted) { return Promise.reject((0, _rxError.newRxError)('DOC13', { document: this, id: this.primary })); } var deletedData = (0, _index.flatClone)(this._data); var removedDocData; return collection._runHooks('pre', 'remove', deletedData, this).then(async () => { deletedData._deleted = true; var writeRows = [{ previous: this._data, document: deletedData }]; var writeResult = await collection.storageInstance.bulkWrite(writeRows, 'rx-document-remove'); var isError = writeResult.error[0]; (0, _rxStorageHelper.throwIfIsStorageWriteError)(collection, this.primary, deletedData, isError); return (0, _rxStorageHelper.getWrittenDocumentsFromBulkWriteResponse)(this.collection.schema.primaryPath, writeRows, writeResult)[0]; }).then(removed => { removedDocData = removed; return this.collection._runHooks('post', 'remove', deletedData, this); }).then(() => { return this.collection._docCache.getCachedRxDocument(removedDocData); }); }, incrementalRemove() { return this.incrementalModify(async docData => { await this.collection._runHooks('pre', 'remove', docData, this); docData._deleted = true; return docData; }).then(async newDoc => { await this.collection._runHooks('post', 'remove', newDoc._data, newDoc); return newDoc; }); }, destroy() { throw (0, _rxError.newRxError)('DOC14'); } }; function createRxDocumentConstructor(proto = basePrototype) { var constructor = function RxDocumentConstructor(collection, docData) { this.collection = collection; // assume that this is always equal to the doc-data in the database this._data = docData; this._propertyCache = new Map(); /** * because of the prototype-merge, * we can not use the native instanceof operator */ this.isInstanceOfRxDocument = true; }; constructor.prototype = proto; return constructor; } function createWithConstructor(constructor, collection, jsonData) { var doc = new constructor(collection, jsonData); (0, _hooks.runPluginHooks)('createRxDocument', doc); return doc; } function isRxDocument(obj) { return typeof obj === 'object' && obj !== null && 'isInstanceOfRxDocument' in obj; } function beforeDocumentUpdateWrite(collection, newData, oldData) { /** * Meta values must always be merged * instead of overwritten. * This ensures that different plugins do not overwrite * each others meta properties. */ newData._meta = Object.assign({}, oldData._meta, newData._meta); // ensure modifications are ok if (_overwritable.overwritable.isDevMode()) { collection.schema.validateChange(oldData, newData); } return collection._runHooks('pre', 'save', newData, oldData); } function getDocumentProperty(doc, objPath) { return (0, _index.getFromMapOrCreate)(doc._propertyCache, objPath, () => { var valueObj = (0, _index.getProperty)(doc._data, objPath); // direct return if array or non-object if (typeof valueObj !== 'object' || valueObj === null || Array.isArray(valueObj)) { return _overwritable.overwritable.deepFreezeWhenDevMode(valueObj); } var proxy = new Proxy( /** * In dev-mode, the _data is deep-frozen * so we have to flat clone here so that * the proxy can work. */ (0, _index.flatClone)(valueObj), { /** * @performance is really important here * because people access nested properties very often * and might not be aware that this is internally using a Proxy */ get(target, property) { if (typeof property !== 'string') { return target[property]; } var lastChar = property.charAt(property.length - 1); if (lastChar === '$') { if (property.endsWith('$$')) { var key = property.slice(0, -2); return doc.get$$((0, _index.trimDots)(objPath + '.' + key)); } else { var _key = property.slice(0, -1); return doc.get$((0, _index.trimDots)(objPath + '.' + _key)); } } else if (lastChar === '_') { var _key2 = property.slice(0, -1); return doc.populate((0, _index.trimDots)(objPath + '.' + _key2)); } else { /** * Performance shortcut * In most cases access to nested properties * will only access simple values which can be directly returned * without creating a new Proxy or utilizing the cache. */ var plainValue = target[property]; if (typeof plainValue === 'number' || typeof plainValue === 'string' || typeof plainValue === 'boolean') { return plainValue; } return getDocumentProperty(doc, (0, _index.trimDots)(objPath + '.' + property)); } } }); return proxy; }); } ; //# sourceMappingURL=rx-document.js.map