diff --git a/config/webpack.config.js b/config/webpack.config.js index f447023a983..c62ded87043 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -3,8 +3,20 @@ const TerserPlugin = require('terser-webpack-plugin'); const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; -//console.log(process.env.NODE_ENV); -//process.exit(); +/** + * Throw on bailouts. + * If a dependency is causing a bailout, + * it must be replaced! + */ +const oldConsoleLog = console.log.bind(console); +console.log = function (m1, m2, m3) { + if (m1.includes('not an ECMAScript module')) { + oldConsoleLog(m1); + throw new Error('ERROR: A dependency of RxDB is causing an optimization bailout. This is not allowed.'); + } else { + return oldConsoleLog(m1, m2, m3); + } +}; const plugins = []; @@ -30,6 +42,6 @@ module.exports = { * @link https://webpack.js.org/plugins/module-concatenation-plugin/#debugging-optimization-bailouts */ optimizationBailout: true, - warnings: false + warnings: true } }; diff --git a/package.json b/package.json index 5aa527eb2c4..a9a7fae0d50 100644 --- a/package.json +++ b/package.json @@ -383,7 +383,6 @@ "@types/cors": "2.8.13", "@types/express": "4.17.15", "@types/lokijs": "1.5.7", - "@types/object-path": "0.11.1", "@types/simple-peer": "9.11.5", "@types/ws": "8.5.4", "ajv": "8.12.0", @@ -393,8 +392,7 @@ "crypto-js": "4.1.1", "custom-idle-queue": "3.0.1", "dexie": "4.0.0-alpha.4", - "event-reduce-js": "2.0.4", - "fast-deep-equal": "3.1.3", + "event-reduce-js": "2.1.0", "firebase": "9.15.0", "get-graphql-from-jsonschema": "8.1.0", "graphql": "15.8.0", @@ -406,7 +404,6 @@ "lokijs": "1.5.12", "mingo": "6.2.5", "modifyjs": "0.3.1", - "object-path": "0.11.8", "oblivious-set": "1.1.1", "pouchdb-selector-core": "7.3.1", "reconnecting-websocket": "4.4.0", diff --git a/src/plugins/crdt/index.ts b/src/plugins/crdt/index.ts index bd0c0a8df2d..0663ffd9b68 100644 --- a/src/plugins/crdt/index.ts +++ b/src/plugins/crdt/index.ts @@ -1,5 +1,4 @@ import { newRxError } from '../../rx-error'; -import objectPath from 'object-path'; import type { CRDTDocumentField, CRDTEntry, @@ -19,8 +18,10 @@ import { clone, deepEqual, ensureNotFalsy, + getProperty, now, objectPathMonad, + setProperty, toArray } from '../../plugins/utils'; import modifyjs from 'modifyjs'; @@ -50,7 +51,7 @@ export async function updateCRDT( const storageToken = await this.collection.database.storageToken; return this.incrementalModify((docData) => { - const crdtDocField: CRDTDocumentField = clone(objectPath.get(docData as any, crdtOptions.field)); + const crdtDocField: CRDTDocumentField = clone(getProperty(docData as any, crdtOptions.field)); const operation: CRDTOperation = { body: toArray(entry), creator: storageToken, @@ -71,7 +72,7 @@ export async function updateCRDT( docData, operation ); - objectPath.set(docData, crdtOptions.field, crdtDocField); + setProperty(docData, crdtOptions.field, crdtDocField); return docData; }, RX_CRDT_CONTEXT); } @@ -109,7 +110,7 @@ export async function insertCRDT( operations: [], hash: '' }; - objectPath.set(insertData as any, crdtOptions.field, crdtDocField); + setProperty(insertData as any, crdtOptions.field, crdtDocField); const lastAr: CRDTOperation[] = [operation]; crdtDocField.operations.push(lastAr); @@ -288,7 +289,7 @@ export function rebuildFromCRDT( let base: WithDeleted = { _deleted: false } as any; - objectPath.set(base, ensureNotFalsy(schema.crdt).field, crdts); + setProperty(base, ensureNotFalsy(schema.crdt).field, crdts); crdts.operations.forEach(operations => { operations.forEach(op => { base = runOperationOnDocument( @@ -502,7 +503,7 @@ export const RxDBcrdtPlugin: RxPlugin = { hash: '' }; crdtOperations.hash = hashCRDTOperations(collection.database.hashFunction, crdtOperations); - objectPath.set(docData, crdtOptions.field, crdtOperations); + setProperty(docData, crdtOptions.field, crdtOperations); return docData; }); return bulkInsertBefore(useDocsData); diff --git a/src/plugins/dev-mode/check-schema.ts b/src/plugins/dev-mode/check-schema.ts index 24a84d8e32f..1db949c20e8 100644 --- a/src/plugins/dev-mode/check-schema.ts +++ b/src/plugins/dev-mode/check-schema.ts @@ -2,8 +2,6 @@ * does additional checks over the schema-json * to ensure nothing is broken or not supported */ - -import objectPath from 'object-path'; import { newRxError } from '../../rx-error'; @@ -16,7 +14,7 @@ import type { TopLevelProperty } from '../../types'; import { - flattenObject, isMaybeReadonlyArray, + flattenObject, getProperty, isMaybeReadonlyArray, trimDots } from '../../plugins/utils'; import { rxDocumentProperties } from './entity-properties'; @@ -458,8 +456,8 @@ export function checkSchema(jsonSchema: RxJsonSchema) { .filter(key => key !== '') .filter((elem, pos, arr) => arr.indexOf(elem) === pos) // unique .filter(key => { // check if this path defines an index - const value = objectPath.get(jsonSchema, key); - return !!value.index; + const value = getProperty(jsonSchema, key); + return value && !!value.index; }) .forEach(key => { // replace inner properties key = key.replace('properties.', ''); // first @@ -483,7 +481,7 @@ export function checkSchema(jsonSchema: RxJsonSchema) { .filter((elem, pos, arr) => arr.indexOf(elem) === pos) // from now on working only with unique indexes .map(indexPath => { const realPath = getSchemaPropertyRealPath(indexPath); // real path in the collection schema - const schemaObj = objectPath.get(jsonSchema, realPath); // get the schema of the indexed property + const schemaObj = getProperty(jsonSchema, realPath); // get the schema of the indexed property if (!schemaObj || typeof schemaObj !== 'object') { throw newRxError('SC21', { index: indexPath, @@ -524,8 +522,8 @@ export function checkSchema(jsonSchema: RxJsonSchema) { .filter((elem, pos, arr) => arr.indexOf(elem) === pos) // unique .filter(key => { // check if this path defines an encrypted field - const value = objectPath.get(jsonSchema, key); - return !!value.encrypted; + const value = getProperty(jsonSchema, key); + return value && !!value.encrypted; }) .forEach(key => { // replace inner properties key = key.replace('properties.', ''); // first @@ -543,7 +541,7 @@ export function checkSchema(jsonSchema: RxJsonSchema) { // real path in the collection schema const realPath = getSchemaPropertyRealPath(propPath); // get the schema of the indexed property - const schemaObj = objectPath.get(jsonSchema, realPath); + const schemaObj = getProperty(jsonSchema, realPath); if (!schemaObj || typeof schemaObj !== 'object') { throw newRxError('SC28', { field: propPath, diff --git a/src/plugins/encryption/index.ts b/src/plugins/encryption/index.ts index bdac7a45f06..117941aca44 100644 --- a/src/plugins/encryption/index.ts +++ b/src/plugins/encryption/index.ts @@ -5,7 +5,6 @@ */ import AES from 'crypto-js/aes'; import * as cryptoEnc from 'crypto-js/enc-utf8'; -import objectPath from 'object-path'; import { wrapRxStorageInstance } from '../../plugin-helpers'; import { INTERNAL_STORE_SCHEMA_TITLE @@ -26,7 +25,9 @@ import { b64EncodeUnicode, clone, ensureNotFalsy, - flatClone + flatClone, + getProperty, + setProperty } from '../../plugins/utils'; export const MINIMUM_PASSWORD_LENGTH: 8 = 8; @@ -121,14 +122,14 @@ export function wrappedKeyEncryptionStorage( docData = cloneWithoutAttachments(docData); ensureNotFalsy(params.schema.encrypted) .forEach(path => { - const value = objectPath.get(docData, path); + const value = getProperty(docData, path); if (typeof value === 'undefined') { return; } const stringValue = JSON.stringify(value); const encrypted = encryptString(stringValue, password); - objectPath.set(docData, path, encrypted); + setProperty(docData, path, encrypted); }); // handle attachments @@ -153,13 +154,13 @@ export function wrappedKeyEncryptionStorage( docData = cloneWithoutAttachments(docData); ensureNotFalsy(params.schema.encrypted) .forEach(path => { - const value = objectPath.get(docData, path); + const value = getProperty(docData, path); if (typeof value === 'undefined') { return; } const decrypted = decryptString(value, password); const decryptedParsed = JSON.parse(decrypted); - objectPath.set(docData, path, decryptedParsed); + setProperty(docData, path, decryptedParsed); }); return docData; } diff --git a/src/plugins/local-documents/rx-local-document.ts b/src/plugins/local-documents/rx-local-document.ts index cbe3693a092..7c17a0246cb 100644 --- a/src/plugins/local-documents/rx-local-document.ts +++ b/src/plugins/local-documents/rx-local-document.ts @@ -1,4 +1,3 @@ -import objectPath from 'object-path'; import { Observable } from 'rxjs'; import { distinctUntilChanged, @@ -34,6 +33,7 @@ import { getDefaultRxDocumentMeta, getFromMapOrThrow, getFromObjectOrThrow, + getProperty, RXJS_SHARE_REPLAY_DEFAULTS } from '../../plugins/utils'; import { getLocalDocStateByParent, LOCAL_DOC_STATE_BY_PARENT_RESOLVED } from './local-documents-helper'; @@ -100,7 +100,7 @@ const RxLocalDocumentPrototype: any = { }); } - let valueObj = objectPath.get(this._data, objPath); + let valueObj = getProperty(this._data, objPath); valueObj = overwritable.deepFreezeWhenDevMode(valueObj); return valueObj; }, @@ -119,7 +119,7 @@ const RxLocalDocumentPrototype: any = { } return this.$ .pipe( - map(data => objectPath.get(data, objPath)), + map(data => getProperty(data, objPath)), distinctUntilChanged() ); }, diff --git a/src/plugins/replication-graphql/index.ts b/src/plugins/replication-graphql/index.ts index 04162dda833..3a8cb508c44 100644 --- a/src/plugins/replication-graphql/index.ts +++ b/src/plugins/replication-graphql/index.ts @@ -2,10 +2,10 @@ * this plugin adds the RxCollection.syncGraphQl()-function to rxdb * you can use it to sync collections with a remote graphql endpoint. */ -import objectPath from 'object-path'; import { ensureNotFalsy, - fastUnsecureHash + fastUnsecureHash, + getProperty } from '../../plugins/utils'; import { @@ -127,10 +127,8 @@ export function replicateGraphQL( if (result.errors) { throw result.errors; } - const dataPath = pull.dataPath || ['data', Object.keys(result.data)[0]]; - let data: any = objectPath.get(result, dataPath); - + let data: any = getProperty(result, dataPath); if (pull.responseModifier) { data = await pull.responseModifier( data, @@ -165,7 +163,7 @@ export function replicateGraphQL( throw result.errors; } const dataPath = Object.keys(result.data)[0]; - const data: any = objectPath.get(result.data, dataPath); + const data: any = getProperty(result.data, dataPath); return data; }, batchSize: push.batchSize, diff --git a/src/plugins/storage-lokijs/lokijs-helper.ts b/src/plugins/storage-lokijs/lokijs-helper.ts index bb66462a475..ce5ed465a34 100644 --- a/src/plugins/storage-lokijs/lokijs-helper.ts +++ b/src/plugins/storage-lokijs/lokijs-helper.ts @@ -15,11 +15,10 @@ import { add as unloadAdd, AddReturn } from 'unload'; -import { ensureNotFalsy, flatClone, promiseWait, randomCouchString } from '../utils'; +import { ensureNotFalsy, flatClone, getProperty, promiseWait, randomCouchString } from '../utils'; import { LokiSaveQueue } from './loki-save-queue'; import type { DeterministicSortComparator } from 'event-reduce-js'; import { newRxError } from '../../rx-error'; -import objectPath from 'object-path'; import { LeaderElector, OnMessageHandler @@ -225,8 +224,8 @@ export function getLokiSortComparator( const fieldName: string = Object.keys(sortPart)[0]; const direction: MangoQuerySortDirection = Object.values(sortPart)[0]; const directionMultiplier = direction === 'asc' ? 1 : -1; - const valueA: any = objectPath.get(a as any, fieldName); - const valueB: any = objectPath.get(b as any, fieldName); + const valueA: any = getProperty(a as any, fieldName); + const valueB: any = getProperty(b as any, fieldName); if (valueA === valueB) { return false; } else { diff --git a/src/plugins/utils/index.ts b/src/plugins/utils/index.ts index 5f496bd558b..b7c1d2c0968 100644 --- a/src/plugins/utils/index.ts +++ b/src/plugins/utils/index.ts @@ -6,6 +6,8 @@ export * from './utils-document'; export * from './utils-hash'; export * from './utils-promise'; export * from './utils-string'; +export * from './utils-object-deep-equal'; +export * from './utils-object-dot-prop'; export * from './utils-object'; export * from './utils-error'; export * from './utils-time'; diff --git a/src/plugins/utils/utils-object-deep-equal.ts b/src/plugins/utils/utils-object-deep-equal.ts new file mode 100644 index 00000000000..8663fcfa83f --- /dev/null +++ b/src/plugins/utils/utils-object-deep-equal.ts @@ -0,0 +1,46 @@ + +/** + * Copied from the fast-deep-equal package + * because it does not support es modules and causes optimization bailouts. + * TODO use the npm package again when this is merged: + * @link https://github.com/epoberezkin/fast-deep-equal/pull/105 + */ +export function deepEqual(a: any, b: any): boolean { + if (a === b) return true; + + if (a && b && typeof a == 'object' && typeof b == 'object') { + if (a.constructor !== b.constructor) return false; + + let length; + let i; + if (Array.isArray(a)) { + length = a.length; + if (length !== b.length) return false; + for (i = length; i-- !== 0;) + if (!deepEqual(a[i], b[i])) return false; + return true; + } + + + if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags; + if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf(); + if (a.toString !== Object.prototype.toString) return a.toString() === b.toString(); + + const keys = Object.keys(a); + length = keys.length; + if (length !== Object.keys(b).length) return false; + + for (i = length; i-- !== 0;) + if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; + + for (i = length; i-- !== 0;) { + const key = keys[i]; + if (!deepEqual(a[key], b[key])) return false; + } + + return true; + } + + // true if both NaN, false otherwise + return a !== a && b !== b; +} diff --git a/src/plugins/utils/utils-object-dot-prop.ts b/src/plugins/utils/utils-object-dot-prop.ts new file mode 100644 index 00000000000..38ca701d63f --- /dev/null +++ b/src/plugins/utils/utils-object-dot-prop.ts @@ -0,0 +1,350 @@ +/** + * Copied from + * @link https://github.com/sindresorhus/dot-prop/blob/main/index.js + * because it is currently an esm only module. + * TODO use the npm package again when RxDB is also fully esm. + */ + +const isObject = (value: null) => { + const type = typeof value; + return value !== null && (type === 'object' || type === 'function'); +}; + +const disallowedKeys = new Set([ + '__proto__', + 'prototype', + 'constructor', +]); + +const digits = new Set('0123456789'); + +function getPathSegments(path: string) { + const parts = []; + let currentSegment = ''; + let currentPart = 'start'; + let isIgnoring = false; + + for (const character of path) { + switch (character) { + case '\\': { + if (currentPart === 'index') { + throw new Error('Invalid character in an index'); + } + + if (currentPart === 'indexEnd') { + throw new Error('Invalid character after an index'); + } + + if (isIgnoring) { + currentSegment += character; + } + + currentPart = 'property'; + isIgnoring = !isIgnoring; + break; + } + + case '.': { + if (currentPart === 'index') { + throw new Error('Invalid character in an index'); + } + + if (currentPart === 'indexEnd') { + currentPart = 'property'; + break; + } + + if (isIgnoring) { + isIgnoring = false; + currentSegment += character; + break; + } + + if (disallowedKeys.has(currentSegment)) { + return []; + } + + parts.push(currentSegment); + currentSegment = ''; + currentPart = 'property'; + break; + } + + case '[': { + if (currentPart === 'index') { + throw new Error('Invalid character in an index'); + } + + if (currentPart === 'indexEnd') { + currentPart = 'index'; + break; + } + + if (isIgnoring) { + isIgnoring = false; + currentSegment += character; + break; + } + + if (currentPart === 'property') { + if (disallowedKeys.has(currentSegment)) { + return []; + } + + parts.push(currentSegment); + currentSegment = ''; + } + + currentPart = 'index'; + break; + } + + case ']': { + if (currentPart === 'index') { + parts.push(Number.parseInt(currentSegment, 10)); + currentSegment = ''; + currentPart = 'indexEnd'; + break; + } + + if (currentPart === 'indexEnd') { + throw new Error('Invalid character after an index'); + } + + // Falls through + } + + default: { + if (currentPart === 'index' && !digits.has(character)) { + throw new Error('Invalid character in an index'); + } + + if (currentPart === 'indexEnd') { + throw new Error('Invalid character after an index'); + } + + if (currentPart === 'start') { + currentPart = 'property'; + } + + if (isIgnoring) { + isIgnoring = false; + currentSegment += '\\'; + } + + currentSegment += character; + } + } + } + + if (isIgnoring) { + currentSegment += '\\'; + } + + switch (currentPart) { + case 'property': { + if (disallowedKeys.has(currentSegment)) { + return []; + } + + parts.push(currentSegment); + + break; + } + + case 'index': { + throw new Error('Index was not closed'); + } + + case 'start': { + parts.push(''); + + break; + } + // No default + } + + return parts; +} + +function isStringIndex(object: any[], key: string) { + if (typeof key !== 'number' && Array.isArray(object)) { + const index = Number.parseInt(key, 10); + return Number.isInteger(index) && object[index] === object[key as any]; + } + + return false; +} + +function assertNotStringIndex(object: any, key: string | number) { + if (isStringIndex(object, key as any)) { + throw new Error('Cannot use string index'); + } +} + +export function getProperty(object: any, path: string | string[], value?: any) { + if (Array.isArray(path)) { + path = path.join('.'); + } + + if (!isObject(object as any) || typeof path !== 'string') { + return value === undefined ? object : value; + } + + const pathArray = getPathSegments(path); + if (pathArray.length === 0) { + return value; + } + + for (let index = 0; index < pathArray.length; index++) { + const key = pathArray[index]; + + if (isStringIndex(object as any, key as any)) { + object = index === pathArray.length - 1 ? undefined : null; + } else { + object = (object as any)[key]; + } + + if (object === undefined || object === null) { + // `object` is either `undefined` or `null` so we want to stop the loop, and + // if this is not the last bit of the path, and + // if it didn't return `undefined` + // it would return `null` if `object` is `null` + // but we want `get({foo: null}, 'foo.bar')` to equal `undefined`, or the supplied value, not `null` + if (index !== pathArray.length - 1) { + return value; + } + + break; + } + } + + return object === undefined ? value : object; +} + +export function setProperty(object: any, path: string, value: any) { + if (Array.isArray(path)) { + path = path.join('.'); + } + + if (!isObject(object as any) || typeof path !== 'string') { + return object; + } + + const root = object; + const pathArray = getPathSegments(path); + + for (let index = 0; index < pathArray.length; index++) { + const key = pathArray[index]; + + assertNotStringIndex(object, key); + + if (index === pathArray.length - 1) { + object[key] = value; + } else if (!isObject(object[key])) { + object[key] = typeof pathArray[index + 1] === 'number' ? [] : {}; + } + + object = object[key]; + } + + return root; +} + +export function deleteProperty(object: any, path: string) { + if (!isObject(object as any) || typeof path !== 'string') { + return false; + } + + const pathArray = getPathSegments(path); + + for (let index = 0; index < pathArray.length; index++) { + const key = pathArray[index]; + + assertNotStringIndex(object, key); + + if (index === pathArray.length - 1) { + delete object[key]; + return true; + } + + object = object[key]; + + if (!isObject(object as any)) { + return false; + } + } +} + +export function hasProperty(object: any, path: string) { + if (!isObject(object) || typeof path !== 'string') { + return false; + } + + const pathArray = getPathSegments(path); + if (pathArray.length === 0) { + return false; + } + + for (const key of pathArray) { + if (!isObject(object) || !(key in object) || isStringIndex(object, key as any)) { + return false; + } + + object = object[key]; + } + + return true; +} + +// TODO: Backslashes with no effect should not be escaped +function escapePath(path: string) { + if (typeof path !== 'string') { + throw new TypeError('Expected a string'); + } + + return path.replace(/[\\.[]/g, '\\$&'); +} + +// The keys returned by Object.entries() for arrays are strings +function entries(value: any) { + if (Array.isArray(value)) { + return value.map((v, index) => [index, v]); + } + + return Object.entries(value); +} + +function stringifyPath(pathSegments: never[]) { + let result = ''; + + // eslint-disable-next-line prefer-const + for (let [index, segment] of entries(pathSegments)) { + if (typeof segment === 'number') { + result += `[${segment}]`; + } else { + segment = escapePath(segment); + result += index === 0 ? segment : `.${segment}`; + } + } + + return result; +} + +function* deepKeysIterator(object: any, currentPath = []): any { + if (!isObject(object)) { + if (currentPath.length > 0) { + yield stringifyPath(currentPath); + } + + return; + } + + for (const [key, value] of entries(object)) { + yield* deepKeysIterator(value, [...currentPath, key] as any); + } +} + +export function deepKeys(object: any) { + return [...deepKeysIterator(object)]; +} diff --git a/src/plugins/utils/utils-object.ts b/src/plugins/utils/utils-object.ts index 4507b24e02c..ad933d8745e 100644 --- a/src/plugins/utils/utils-object.ts +++ b/src/plugins/utils/utils-object.ts @@ -1,7 +1,6 @@ import type { DeepReadonlyObject } from '../../types'; -import equal from 'fast-deep-equal'; export function deepFreeze(o: T): T { Object.freeze(o); @@ -26,14 +25,10 @@ export function deepFreeze(o: T): T { } -export function deepEqual(obj1: T, obj2: T): boolean { - return equal(obj1, obj2); -} - /** * To get specific nested path values from objects, - * RxDB normally uses the 'object-path' npm module. + * RxDB normally uses the 'dot-prop' npm module. * But when performance is really relevant, this is not fast enough. * Instead we use a monad that can prepare some stuff up front * and we can re-use the generated function. diff --git a/src/rx-document.ts b/src/rx-document.ts index 9059ac6b00f..b5f167c6135 100644 --- a/src/rx-document.ts +++ b/src/rx-document.ts @@ -1,4 +1,3 @@ -import objectPath from 'object-path'; import { Observable } from 'rxjs'; @@ -16,7 +15,8 @@ import { flatClone, PROMISE_RESOLVE_NULL, RXJS_SHARE_REPLAY_DEFAULTS, - getFromObjectOrThrow + getFromObjectOrThrow, + getProperty } from './plugins/utils'; import { newRxError @@ -134,7 +134,7 @@ export const basePrototype = { return this.$ .pipe( - map(data => objectPath.get(data, path)), + map(data => getProperty(data, path)), distinctUntilChanged() ); }, @@ -187,7 +187,7 @@ export const basePrototype = { */ get(this: RxDocument, objPath: string): any | null { if (!this._data) return undefined; - let valueObj = objectPath.get(this._data, objPath); + let valueObj = getProperty(this._data, objPath); // direct return if array or non-object if ( diff --git a/src/rx-schema-helper.ts b/src/rx-schema-helper.ts index f009dcbf742..26dd55d2faf 100644 --- a/src/rx-schema-helper.ts +++ b/src/rx-schema-helper.ts @@ -1,4 +1,3 @@ -import objectPath from 'object-path'; import { newRxError } from './rx-error'; import type { CompositePrimaryKey, @@ -12,6 +11,7 @@ import type { } from './types'; import { flatClone, + getProperty, isMaybeReadonlyArray, RX_META_LWT_MINIMUM, sortObject, @@ -53,7 +53,7 @@ export function getSchemaByObjectPath( usePath = 'properties.' + usePath; usePath = trimDots(usePath); - const ret = objectPath.get(rxJsonSchema, usePath); + const ret = getProperty(rxJsonSchema, usePath); return ret; } @@ -111,7 +111,7 @@ export function getComposedPrimaryKeyOfDocumentData( const compositePrimary: CompositePrimaryKey = jsonSchema.primaryKey as any; return compositePrimary.fields.map(field => { - const value = objectPath.get(documentData as any, field as string); + const value = getProperty(documentData as any, field as string); if (typeof value === 'undefined') { throw newRxError('DOC18', { args: { field, documentData } }); } diff --git a/test/unit/replication-protocol.test.ts b/test/unit/replication-protocol.test.ts index f505ddf0b3d..a08a8dbaf9f 100644 --- a/test/unit/replication-protocol.test.ts +++ b/test/unit/replication-protocol.test.ts @@ -26,7 +26,8 @@ import { awaitRxStorageReplicationInSync, defaultHashFunction, getComposedPrimaryKeyOfDocumentData, - setCheckpoint + setCheckpoint, + deepEqual } from '../../'; @@ -35,7 +36,6 @@ import { RX_LOCAL_DOCUMENT_SCHEMA } from '../../plugins/local-documents'; import * as schemas from '../helper/schemas'; -import deepEqual from 'fast-deep-equal'; import { clone,