Socket
Socket
Sign inDemoInstall

redux-orm

Package Overview
Dependencies
Maintainers
1
Versions
70
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

redux-orm - npm Package Compare versions

Comparing version 0.1.22 to 0.1.23

.publish/global.html

23

lib/Backend.js

@@ -224,3 +224,7 @@ 'use strict';

mapFunction = function (entity) {
return Object.assign({}, entity, patcher);
var diff = (0, _utils.objectDiff)(entity, patcher);
if (diff) {
return Object.assign({}, entity, patcher);
}
return entity;
};

@@ -237,12 +241,23 @@ }

idArr.reduce(function (map, id) {
map[id] = mapFunction(branch[mapName][id]);
var result = mapFunction(branch[mapName][id]);
if (result !== branch[mapName][id]) map[id] = result;
return map;
}, updatedMap);
Object.assign(returnBranch[mapName], updatedMap);
var diff = (0, _utils.objectDiff)(returnBranch[mapName], updatedMap);
if (diff) {
Object.assign(returnBranch[mapName], diff);
} else {
return branch;
}
return returnBranch;
}
var updated = false;
returnBranch[arrName] = branch[arrName].map(function (entity) {
if (idArr.includes(entity[idAttribute])) {
var result = mapFunction(entity);
if (entity !== result) {
updated = true;
}
return mapFunction(entity);

@@ -252,3 +267,3 @@ }

});
return returnBranch;
return updated ? returnBranch : branch;
}

@@ -255,0 +270,0 @@

22

lib/descriptors.js

@@ -98,11 +98,5 @@ 'use strict';

var throughQs = throughModel.filter(lookupObj);
var toIdsSet = {};
throughQs.plain.objects().forEach(function (throughObject) {
var id = throughObject[reverse ? fromFieldName : toFieldName];
if (typeof id !== 'undefined') {
toIdsSet[id] = true;
}
var toIds = throughQs.plain.map(function (obj) {
return obj[reverse ? fromFieldName : toFieldName];
});
var toIds = Object.keys(toIdsSet);

@@ -137,13 +131,5 @@ var qsFromModel = reverse ? declaredFromModel : declaredToModel;

var attrShouldMatchThisId = reverse ? toFieldName : fromFieldName;
var attrInIdsToRemove = reverse ? fromFieldName : toFieldName;
var entitiesToDelete = throughModel.filter(function (through) {
if (through[attrShouldMatchThisId] === thisId) {
if (idsToRemove.includes(through[attrInIdsToRemove])) {
return true;
}
}
return false;
var entitiesToDelete = throughQs.plain.filter(function (through) {
return idsToRemove.includes(through[attrInIdsToRemove]);
});

@@ -150,0 +136,0 @@

@@ -201,9 +201,25 @@ 'use strict';

if (field instanceof _fields.ManyToMany) {
var _mergeKey;
var currentIds = this[mergeKey].idArr;
// TODO: instead of clearing the old records,
// we should check which ones can remain.
this[mergeKey].clear();
(_mergeKey = this[mergeKey]).add.apply(_mergeKey, _toConsumableArray(mergeObj[mergeKey]));
// TODO: It could be better to check this stuff in Backend.
var normalizedNewIds = mergeObj[mergeKey].map(_utils.normalizeEntity);
var diffActions = (0, _utils.arrayDiffActions)(currentIds, normalizedNewIds);
if (diffActions) {
var idsToDelete = diffActions['delete'];
var idsToAdd = diffActions.add;
if (idsToDelete.length > 0) {
var _mergeKey;
(_mergeKey = this[mergeKey]).remove.apply(_mergeKey, _toConsumableArray(idsToDelete));
}
if (idsToAdd.length > 0) {
var _mergeKey2;
(_mergeKey2 = this[mergeKey]).add.apply(_mergeKey2, _toConsumableArray(idsToAdd));
}
}
delete mergeObj[mergeKey];
} else if (field instanceof _fields.ForeignKey || field instanceof _fields.OneToOne) {
mergeObj[mergeKey] = (0, _utils.normalizeEntity)(mergeObj[mergeKey]);
}

@@ -387,2 +403,7 @@ }

}
}, {
key: 'markAccessed',
value: function markAccessed() {
this.session.markAccessed(this);
}

@@ -406,2 +427,3 @@ /**

value: function accessId(id) {
this.markAccessed();
return this.getBackend().accessId(this.state, id);

@@ -417,2 +439,3 @@ }

value: function accessIds() {
this.markAccessed();
return this.getBackend().accessIdList(this.state);

@@ -423,2 +446,3 @@ }

value: function accessList() {
this.markAccessed();
return this.getBackend().accessList(this.state);

@@ -429,2 +453,3 @@ }

value: function iterator() {
this.markAccessed();
return this.getBackend().iterator(this.state);

@@ -431,0 +456,0 @@ }

@@ -19,2 +19,4 @@ 'use strict';

var _reselect = require('reselect');
var _lodashObjectForOwn = require('lodash/object/forOwn');

@@ -40,2 +42,4 @@

var _memoize = require('./memoize');
var _utils = require('./utils');

@@ -60,2 +64,3 @@

this.implicitThroughModels = [];
this.selectorCreator = (0, _reselect.createSelectorCreator)(_memoize.memoize, _memoize.eqCheck, this);
}

@@ -343,2 +348,54 @@

}
/**
* Returns a memoized selector based on passed arguments.
* This is similar to `reselect`'s `createSelector`,
* except you can also pass a single function to be memoized.
*
* If you pass multiple functions, the format will be the
* same as in `reselect`. The last argument is the selector
* function and the previous are input selectors.
*
* When you use this method to create a selector, the returned selector
* expects the whole `redux-orm` state branch as input. In the selector
* function that you pass as the last argument, you will receive
* `session` argument (a `Session` instance) followed by any
* input arguments, like in `reselect`.
*
* This is an example selector:
*
* ```javascript
* const bookSelector = schema.createSelector(session => {
* return session.Book.map(book => {
* return Object.assign(book.toPlain(), {
* authors: book.authors.map(author => author.name),
* genres: book.genres.map(genre => genre.name),
* });
* });
* });
* ```
*
* redux-orm uses a special memoization function to avoid recomputations.
* When a selector runs for the first time, it checks which Models' state
* branches were accessed. On subsequent runs, the selector first checks
* if those branches have changed -- if not, it just returns the previous
* result. This way you can use the `PureRenderMixin` in your React
* components for performance gains.
*
* @param {...Function} args - zero or more input selectors
* and the selector function.
* @return {Function} memoized selector
*/
}, {
key: 'createSelector',
value: function createSelector() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
if (args.length === 1) {
return (0, _memoize.memoize)(args[0], _memoize.eqCheck, this);
}
return this.selectorCreator.apply(this, args);
}
}]);

@@ -345,0 +402,0 @@

@@ -46,2 +46,4 @@ 'use strict';

this._accessedModels = {};
models.forEach(function (modelClass) {

@@ -58,12 +60,17 @@ Object.defineProperty(_this, modelClass.modelName, {

/**
* Records an update to the session.
* @param {Object} update - the update object. Must have keys
* `type`, `payload` and `meta`. `meta`
* must also include a `name` attribute
* that contains the model name.
*/
_createClass(Session, [{
key: 'markAccessed',
value: function markAccessed(model) {
this._accessedModels[model.modelName] = true;
}
}, {
key: 'addUpdate',
/**
* Records an update to the session.
* @param {Object} update - the update object. Must have keys
* `type`, `payload` and `meta`. `meta`
* must also include a `name` attribute
* that contains the model name.
*/
value: function addUpdate(update) {

@@ -136,2 +143,7 @@ if (this.withMutations) {

}
}, {
key: 'accessedModels',
get: function get() {
return Object.keys(this._accessedModels);
}
}]);

@@ -138,0 +150,0 @@

@@ -93,2 +93,4 @@ 'use strict';

var backendMock = undefined;
var sessionMock = undefined;
var markAccessedSpy = undefined;
var stateMock = {};

@@ -99,2 +101,4 @@

Model.modelName = 'Model';
markAccessedSpy = _sinon2['default'].spy();
sessionMock = { markAccessed: markAccessedSpy };
backendMock = {};

@@ -104,2 +108,3 @@ Model.getBackend = function () {

};
Model._session = sessionMock;
Object.defineProperty(Model, 'state', {

@@ -121,2 +126,3 @@ get: function get() {

expect(accessIdSpy).to.have.been.calledWithExactly(stateMock, arg);
expect(markAccessedSpy).to.have.been.calledOnce;
});

@@ -132,2 +138,3 @@

expect(accessIdsSpy).to.have.been.calledWithExactly(stateMock);
expect(markAccessedSpy).to.have.been.calledOnce;
});

@@ -143,2 +150,3 @@

expect(accessIdsSpy).to.have.been.calledWithExactly(stateMock);
expect(markAccessedSpy).to.have.been.calledOnce;
});

@@ -145,0 +153,0 @@ });

@@ -401,7 +401,14 @@ 'use strict';

var SF = orm.Location.get({ name: 'San Francisco' });
(0, _chai.expect)(orm.accessedModels).to.deep.equal(['Location']);
(0, _chai.expect)(SF.personSet.count()).to.equal(1);
var hki = orm.Location.get({ name: 'Helsinki' });
(0, _chai.expect)(orm.accessedModels).to.deep.equal(['Location', 'PersonLocations']);
(0, _chai.expect)(hki.personSet.count()).to.equal(2);
hki.personSet.map(function (x) {
return x;
});
(0, _chai.expect)(orm.accessedModels).to.deep.equal(['Location', 'PersonLocations', 'Person']);
var tommi = orm.Person.first();

@@ -408,0 +415,0 @@ (0, _chai.expect)(tommi.name).to.equal('Tommi');

@@ -1,8 +0,1 @@

/**
* @module utils
*/
/**
* A simple ListIterator implementation.
*/
'use strict';

@@ -16,4 +9,26 @@

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _lodashObjectForOwn = require('lodash/object/forOwn');
var _lodashObjectForOwn2 = _interopRequireDefault(_lodashObjectForOwn);
var _lodashArrayIntersection = require('lodash/array/intersection');
var _lodashArrayIntersection2 = _interopRequireDefault(_lodashArrayIntersection);
var _lodashArrayDifference = require('lodash/array/difference');
var _lodashArrayDifference2 = _interopRequireDefault(_lodashArrayDifference);
/**
* @module utils
*/
/**
* A simple ListIterator implementation.
*/
var ListIterator = (function () {

@@ -206,2 +221,44 @@ /**

/**
* Checks if `target` needs to be merged with
* `source`. Does a shallow equal check on the `source`
* object's own properties against the same
* properties on `target`. If all properties are equal,
* returns `null`. Otherwise returns an object
* with the properties that did not pass
* the equality check. The returned object
* can be used to update `target` immutably
* while sharing more structure.
*
* @param {Object} target - the object to update
* @param {Object} source - the updated props
* @return {Object|null} an object with the inequal props from `source`
* or `null` if no updates or needed.
*/
function objectDiff(target, source) {
var diffObj = {};
var shouldUpdate = false;
(0, _lodashObjectForOwn2['default'])(source, function (value, key) {
if (!target.hasOwnProperty(key) || target[key] !== source[key]) {
shouldUpdate = true;
diffObj[key] = value;
}
});
return shouldUpdate ? diffObj : null;
}
function arrayDiffActions(targetArr, sourceArr) {
var itemsInBoth = (0, _lodashArrayIntersection2['default'])(targetArr, sourceArr);
var deleteItems = (0, _lodashArrayDifference2['default'])(targetArr, itemsInBoth);
var addItems = (0, _lodashArrayDifference2['default'])(sourceArr, itemsInBoth);
if (deleteItems.length || addItems.length) {
return {
'delete': deleteItems,
add: addItems
};
}
return null;
}
exports.match = match;

@@ -214,2 +271,4 @@ exports.attachQuerySetMethods = attachQuerySetMethods;

exports.ListIterator = ListIterator;
exports.normalizeEntity = normalizeEntity;
exports.normalizeEntity = normalizeEntity;
exports.objectDiff = objectDiff;
exports.arrayDiffActions = arrayDiffActions;
{
"name": "redux-orm",
"version": "0.1.22",
"version": "0.1.23",
"description": "Simple ORM to manage and query your state trees",

@@ -35,4 +35,5 @@ "main": "lib/index.js",

"dependencies": {
"lodash": "^3.10.1"
"lodash": "^3.10.1",
"reselect": "^2.0.1"
}
}

@@ -39,4 +39,4 @@ redux-orm

Book.fields = {
authors: many('Author'),
publisher: fk('Publisher'),
authors: many('Author', 'books'),
publisher: fk('Publisher', 'books'),
};

@@ -115,31 +115,53 @@ ```

In your top level component, you can begin a `Session` to query your data with `redux-orm`.
Use memoized selectors to make queries into the state. `redux-orm` uses smart memoization: the below selector accesses `Author` and `AuthorBooks` branches (`AuthorBooks` is a many-to-many branch generated from the model field declarations), and the selector will be recomputed only if those branches change. The accessed branches are resolved on the first run.
```javascript
// components.js
import {Component} from 'React';
// selectors.js
import schema from './schema';
const authorSelector = schema.createSelector(session => {
return session.Author.map(author => {
// Returns a shallow copy of the raw author object,
// so it doesn't include any reverse or m2m fields.
const obj = author.toPlain();
// Object.keys(obj) === ['id', 'name']
class App extends Component {
getORM() {
return schema.from(this.props.orm);
}
return Object.assign(obj, {
books: author.books.plain.map(book => book.name),
});
});
});
render() {
const {
Book,
Publisher,
Author
} = this.getORM();
// Will result in something like this when run:
// [
// {
// id: 0,
// name: 'Tommi Kaikkonen',
// books: ['Introduction to redux-orm', 'Developing Redux applications'],
// },
// {
// id: 1,
// name: 'John Doe',
// books: ['John Doe: an Autobiography']
// }
// ]
```
const authors = Author.map(author => {
// .bookSet is a virtual reverse field,
// generated from book.authors = fk('Author').
const authorBooks = author.bookSet;
const bookNames = authorBooks.map(book => book.name).join(', ');
Selectors created with `schema.createSelector` can be used as input to any additional `reselect` selectors you want to use. They are also great to use with `redux-thunk`: get the whole state with `getState()`, pass the ORM branch to the selector, and get your results. A good use case is serializing data to a custom format for a 3rd party API call.
Because selectors are memoized, you can use pure rendering in React for performance gains.
```javascript
// components.js
import PureComponent from 'react-pure-render/component';
import {authorSelector} from './selectors';
import {connect} from 'react-redux';
class App extends PureComponent {
render() {
const authors = this.props.authors.map(author => {
return (
<li key={author.getId()}>
{author.name} has written {authorBookNames}
</li>);
<li key={author.id}>
{author.name} has written {author.books.join(', ')}
</li>
);
});

@@ -155,2 +177,9 @@

function mapStateToProps(state) {
return {
authors: authorSelector(state.orm),
};
}
export default connect(mapStateToProps)(App);
```

@@ -229,2 +258,3 @@

- `reducer()`: returns a reducer function that can be plugged into Redux. The reducer will return the next state of the database given the provided action. You need to register your models before calling this.
- `createSelector([...inputSelectors], selectorFunc)`: returns a memoized selector function for `selectorFunc`. `selectorFunc` receives `session` as the first argument, followed by any inputs from `inputSelectors`. Read the full documentation for details.

@@ -231,0 +261,0 @@ ### Model

import find from 'lodash/collection/find';
import sortByOrder from 'lodash/collection/sortByOrder';
import omit from 'lodash/object/omit';
import {ListIterator} from './utils';
import {ListIterator, objectDiff} from './utils';

@@ -181,3 +181,9 @@ /**

} else {
mapFunction = (entity) => Object.assign({}, entity, patcher);
mapFunction = (entity) => {
const diff = objectDiff(entity, patcher);
if (diff) {
return Object.assign({}, entity, patcher);
}
return entity;
};
}

@@ -193,12 +199,23 @@

idArr.reduce((map, id) => {
map[id] = mapFunction(branch[mapName][id]);
const result = mapFunction(branch[mapName][id]);
if (result !== branch[mapName][id]) map[id] = result;
return map;
}, updatedMap);
Object.assign(returnBranch[mapName], updatedMap);
const diff = objectDiff(returnBranch[mapName], updatedMap);
if (diff) {
Object.assign(returnBranch[mapName], diff);
} else {
return branch;
}
return returnBranch;
}
let updated = false;
returnBranch[arrName] = branch[arrName].map(entity => {
if (idArr.includes(entity[idAttribute])) {
const result = mapFunction(entity);
if (entity !== result) {
updated = true;
}
return mapFunction(entity);

@@ -208,3 +225,3 @@ }

});
return returnBranch;
return updated ? returnBranch : branch;
}

@@ -211,0 +228,0 @@

@@ -91,12 +91,4 @@ import UPDATE from './constants';

const throughQs = throughModel.filter(lookupObj);
const toIdsSet = {};
const toIds = throughQs.plain.map(obj => obj[reverse ? fromFieldName : toFieldName]);
throughQs.plain.objects().forEach(throughObject => {
const id = throughObject[reverse ? fromFieldName : toFieldName];
if (typeof id !== 'undefined') {
toIdsSet[id] = true;
}
});
const toIds = Object.keys(toIdsSet);
const qsFromModel = reverse ? declaredFromModel : declaredToModel;

@@ -123,13 +115,5 @@ const qs = qsFromModel.getQuerySetFromIds(toIds);

const attrShouldMatchThisId = reverse ? toFieldName : fromFieldName;
const attrInIdsToRemove = reverse ? fromFieldName : toFieldName;
const entitiesToDelete = throughModel.filter(through => {
if (through[attrShouldMatchThisId] === thisId) {
if (idsToRemove.includes(through[attrInIdsToRemove])) {
return true;
}
}
return false;
const entitiesToDelete = throughQs.plain.filter(through => {
return idsToRemove.includes(through[attrInIdsToRemove]);
});

@@ -136,0 +120,0 @@

@@ -16,2 +16,3 @@ import forOwn from 'lodash/object/forOwn';

normalizeEntity,
arrayDiffActions,
} from './utils';

@@ -199,2 +200,6 @@

static markAccessed() {
this.session.markAccessed(this);
}
/**

@@ -218,2 +223,3 @@ * Returns the id attribute of this {@link Model}.

static accessId(id) {
this.markAccessed();
return this.getBackend().accessId(this.state, id);

@@ -227,2 +233,3 @@ }

static accessIds() {
this.markAccessed();
return this.getBackend().accessIdList(this.state);

@@ -232,2 +239,3 @@ }

static accessList() {
this.markAccessed();
return this.getBackend().accessList(this.state);

@@ -237,2 +245,3 @@ }

static iterator() {
this.markAccessed();
return this.getBackend().iterator(this.state);

@@ -508,7 +517,21 @@ }

if (field instanceof ManyToMany) {
// TODO: instead of clearing the old records,
// we should check which ones can remain.
this[mergeKey].clear();
this[mergeKey].add(...mergeObj[mergeKey]);
const currentIds = this[mergeKey].idArr;
// TODO: It could be better to check this stuff in Backend.
const normalizedNewIds = mergeObj[mergeKey].map(normalizeEntity);
const diffActions = arrayDiffActions(currentIds, normalizedNewIds);
if (diffActions) {
const idsToDelete = diffActions.delete;
const idsToAdd = diffActions.add;
if (idsToDelete.length > 0) {
this[mergeKey].remove(...idsToDelete);
}
if (idsToAdd.length > 0) {
this[mergeKey].add(...idsToAdd);
}
}
delete mergeObj[mergeKey];
} else if (field instanceof ForeignKey || field instanceof OneToOne) {
mergeObj[mergeKey] = normalizeEntity(mergeObj[mergeKey]);
}

@@ -515,0 +538,0 @@ }

@@ -0,3 +1,5 @@

import {createSelectorCreator} from 'reselect';
import forOwn from 'lodash/object/forOwn';
import find from 'lodash/collection/find';
import Session from './Session';

@@ -17,2 +19,3 @@ import Model from './Model';

} from './descriptors';
import {memoize, eqCheck} from './memoize';

@@ -27,3 +30,2 @@ import {

/**

@@ -43,2 +45,3 @@ * Schema's responsibility is tracking the set of {@link Model} classes used in the database.

this.implicitThroughModels = [];
this.selectorCreator = createSelectorCreator(memoize, eqCheck, this);
}

@@ -309,4 +312,50 @@

}
/**
* Returns a memoized selector based on passed arguments.
* This is similar to `reselect`'s `createSelector`,
* except you can also pass a single function to be memoized.
*
* If you pass multiple functions, the format will be the
* same as in `reselect`. The last argument is the selector
* function and the previous are input selectors.
*
* When you use this method to create a selector, the returned selector
* expects the whole `redux-orm` state branch as input. In the selector
* function that you pass as the last argument, you will receive
* `session` argument (a `Session` instance) followed by any
* input arguments, like in `reselect`.
*
* This is an example selector:
*
* ```javascript
* const bookSelector = schema.createSelector(session => {
* return session.Book.map(book => {
* return Object.assign(book.toPlain(), {
* authors: book.authors.map(author => author.name),
* genres: book.genres.map(genre => genre.name),
* });
* });
* });
* ```
*
* redux-orm uses a special memoization function to avoid recomputations.
* When a selector runs for the first time, it checks which Models' state
* branches were accessed. On subsequent runs, the selector first checks
* if those branches have changed -- if not, it just returns the previous
* result. This way you can use the `PureRenderMixin` in your React
* components for performance gains.
*
* @param {...Function} args - zero or more input selectors
* and the selector function.
* @return {Function} memoized selector
*/
createSelector(...args) {
if (args.length === 1) {
return memoize(args[0], eqCheck, this);
}
return this.selectorCreator(...args);
}
};
export default Schema;

@@ -25,2 +25,4 @@ import partition from 'lodash/collection/partition';

this._accessedModels = {};
models.forEach(modelClass => {

@@ -35,2 +37,10 @@ Object.defineProperty(this, modelClass.modelName, {

markAccessed(model) {
this._accessedModels[model.modelName] = true;
}
get accessedModels() {
return Object.keys(this._accessedModels);
}
/**

@@ -37,0 +47,0 @@ * Records an update to the session.

@@ -56,2 +56,4 @@ import chai from 'chai';

let backendMock;
let sessionMock;
let markAccessedSpy;
const stateMock = {};

@@ -62,4 +64,7 @@

Model.modelName = 'Model';
markAccessedSpy = sinon.spy();
sessionMock = {markAccessed: markAccessedSpy};
backendMock = {};
Model.getBackend = () => backendMock;
Model._session = sessionMock;
Object.defineProperty(Model, 'state', {

@@ -79,2 +84,3 @@ get: () => stateMock,

expect(accessIdSpy).to.have.been.calledWithExactly(stateMock, arg);
expect(markAccessedSpy).to.have.been.calledOnce;
});

@@ -90,2 +96,3 @@

expect(accessIdsSpy).to.have.been.calledWithExactly(stateMock);
expect(markAccessedSpy).to.have.been.calledOnce;
});

@@ -101,2 +108,3 @@

expect(accessIdsSpy).to.have.been.calledWithExactly(stateMock);
expect(markAccessedSpy).to.have.been.calledOnce;
});

@@ -103,0 +111,0 @@ });

@@ -274,7 +274,12 @@ import {expect} from 'chai';

const SF = orm.Location.get({name: 'San Francisco'});
expect(orm.accessedModels).to.deep.equal(['Location']);
expect(SF.personSet.count()).to.equal(1);
const hki = orm.Location.get({name: 'Helsinki'});
expect(orm.accessedModels).to.deep.equal(['Location', 'PersonLocations']);
expect(hki.personSet.count()).to.equal(2);
hki.personSet.map(x => x);
expect(orm.accessedModels).to.deep.equal(['Location', 'PersonLocations', 'Person']);
const tommi = orm.Person.first();

@@ -281,0 +286,0 @@ expect(tommi.name).to.equal('Tommi');

@@ -0,1 +1,5 @@

import forOwn from 'lodash/object/forOwn';
import intersection from 'lodash/array/intersection';
import difference from 'lodash/array/difference';
/**

@@ -183,2 +187,45 @@ * @module utils

/**
* Checks if `target` needs to be merged with
* `source`. Does a shallow equal check on the `source`
* object's own properties against the same
* properties on `target`. If all properties are equal,
* returns `null`. Otherwise returns an object
* with the properties that did not pass
* the equality check. The returned object
* can be used to update `target` immutably
* while sharing more structure.
*
* @param {Object} target - the object to update
* @param {Object} source - the updated props
* @return {Object|null} an object with the inequal props from `source`
* or `null` if no updates or needed.
*/
function objectDiff(target, source) {
const diffObj = {};
let shouldUpdate = false;
forOwn(source, (value, key) => {
if (!target.hasOwnProperty(key) ||
target[key] !== source[key]) {
shouldUpdate = true;
diffObj[key] = value;
}
});
return shouldUpdate ? diffObj : null;
}
function arrayDiffActions(targetArr, sourceArr) {
const itemsInBoth = intersection(targetArr, sourceArr);
const deleteItems = difference(targetArr, itemsInBoth);
const addItems = difference(sourceArr, itemsInBoth);
if (deleteItems.length || addItems.length) {
return {
delete: deleteItems,
add: addItems,
};
}
return null;
}
export {

@@ -193,2 +240,4 @@ match,

normalizeEntity,
objectDiff,
arrayDiffActions,
};

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc