cspace-ui
Version:
CollectionSpace user interface for browsers
685 lines (658 loc) • 25.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isSavePending = exports.isReadVocabularyItemRefsPending = exports.isReadPending = exports.isModifiedExceptPart = exports.isModified = exports.getValidationErrors = exports.getSubrecordData = exports.getSubrecordCsid = exports.getRelationUpdatedTimestamp = exports.getNewSubrecordCsid = exports.getNewData = exports.getError = exports.getData = exports.default = void 0;
var _immutable = _interopRequireDefault(require("immutable"));
var _get = _interopRequireDefault(require("lodash/get"));
var _actionCodes = require("../constants/actionCodes");
var _recordDataHelpers = require("../helpers/recordDataHelpers");
var _configHelpers = require("../helpers/configHelpers");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const BASE_NEW_RECORD_KEY = '';
const unsavedRecordKey = subrecordName => subrecordName ? `${BASE_NEW_RECORD_KEY}/${subrecordName}` : BASE_NEW_RECORD_KEY;
const getCurrentData = (state, csid) => state.getIn([csid, 'data', 'current']);
const setCurrentData = (state, csid, data) => state.setIn([csid, 'data', 'current'], data);
const getBaselineData = (state, csid) => state.getIn([csid, 'data', 'baseline']);
const setBaselineData = (state, csid, data) => state.setIn([csid, 'data', 'baseline'], data);
const clear = (state, csid, clearSubrecords = false) => {
const recordState = state.get(csid);
if (!recordState) {
return state;
}
let nextState = state;
const subrecord = recordState.get('subrecord');
if (clearSubrecords && subrecord) {
nextState = subrecord.reduce((reducedState, subrecordCsid) => clear(reducedState, subrecordCsid, clearSubrecords), nextState);
}
return nextState.delete(csid);
};
const clearAll = state => state.clear();
const clearFiltered = (state, filter) => {
let nextState = state;
state.filter(filter).forEach((recordState, csid) => {
nextState = clear(nextState, csid);
});
return nextState;
};
const addFieldInstance = (state, action) => {
const {
csid,
path,
position,
recordTypeConfig
} = action.meta;
const data = getCurrentData(state, csid);
if (!data) {
return state;
}
const value = (0, _recordDataHelpers.deepGet)(data, path);
const list = _immutable.default.List.isList(value) ? value : _immutable.default.List.of(value);
const fieldDescriptor = (0, _get.default)(recordTypeConfig, ['fields', ...(0, _configHelpers.dataPathToFieldDescriptorPath)(path)]);
const defaultData = (0, _recordDataHelpers.initializeChildren)(fieldDescriptor, (0, _recordDataHelpers.applyDefaults)(fieldDescriptor));
const updatedList = typeof position === 'undefined' || position < 0 || position >= list.size ? list.push(defaultData) : list.insert(position, defaultData);
const updatedData = (0, _recordDataHelpers.deepSet)(data, path, updatedList);
return setCurrentData(state, csid, updatedData);
};
const sortFieldInstances = (state, action) => {
const {
config,
csid,
path,
byField
} = action.meta;
const data = getCurrentData(state, csid);
if (!data) {
return state;
}
const value = (0, _recordDataHelpers.deepGet)(data, path);
const list = _immutable.default.List.isList(value) ? value : _immutable.default.List.of(value);
// TODO: Check for a custom sort comparator function in field config.
// For now just use the default.
const comparator = (str1, str2) => str1.localeCompare(str2, config.locale);
const sortedList = byField ? list.sortBy(item => item.get(byField), comparator) : list.sort(comparator);
const updatedData = (0, _recordDataHelpers.deepSet)(data, path, sortedList);
return setCurrentData(state, csid, updatedData);
};
const doCreateNew = (state, config, recordTypeConfig, options = {}) => {
const {
cloneCsid,
subrecordName,
stickyFields
} = options;
let data;
if (cloneCsid) {
data = (0, _recordDataHelpers.cloneRecordData)(recordTypeConfig, cloneCsid, getCurrentData(state, cloneCsid));
}
if (!data) {
data = (0, _recordDataHelpers.createRecordData)(recordTypeConfig);
if (stickyFields) {
// Merge in the user's saved sticky fields for this record type, if any.
const fields = stickyFields.get(recordTypeConfig.name);
if (fields) {
data = data.mergeDeep(fields);
}
}
}
const csid = unsavedRecordKey(subrecordName);
let nextState = state.delete(csid);
const {
subrecords
} = recordTypeConfig;
if (subrecords) {
Object.keys(subrecords).forEach(name => {
const subrecordConfig = subrecords[name];
const {
csidField
} = subrecordConfig;
let cloneSubrecordCsid;
if (csidField) {
cloneSubrecordCsid = (0, _recordDataHelpers.deepGet)(data, csidField);
}
if (!cloneSubrecordCsid) {
const subrecordType = subrecordConfig.recordType;
const subrecordTypeConfig = (0, _get.default)(config, ['recordTypes', subrecordType]);
const subrecordCsid = state.getIn([cloneCsid, 'subrecord', name]);
nextState = doCreateNew(nextState, config, subrecordTypeConfig, {
cloneCsid: subrecordCsid,
subrecordName: name,
stickyFields
});
cloneSubrecordCsid = `${csid}/${name}`;
if (csidField) {
data = (0, _recordDataHelpers.deepSet)(data, csidField, cloneSubrecordCsid);
}
}
nextState = nextState.setIn([csid, 'subrecord', name], cloneSubrecordCsid);
});
}
nextState = setBaselineData(nextState, csid, data);
nextState = setCurrentData(nextState, csid, data);
return nextState;
};
const createNewRecord = (state, action) => {
const {
config,
recordTypeConfig,
cloneCsid,
stickyFields
} = action.meta;
return doCreateNew(state, config, recordTypeConfig, {
cloneCsid,
stickyFields
});
};
const deleteFieldValue = (state, action) => {
const {
csid,
path
} = action.meta;
const data = getCurrentData(state, csid);
if (!data) {
return state;
}
const updatedData = (0, _recordDataHelpers.deepDelete)(data, path);
return setCurrentData(state, csid, updatedData);
};
const moveFieldValue = (state, action) => {
const {
csid,
path,
newPosition
} = action.meta;
const data = getCurrentData(state, csid);
if (!data) {
return state;
}
const listPath = path.slice(0, -1);
const oldPosition = path[path.length - 1];
let list = (0, _recordDataHelpers.deepGet)(data, listPath);
if (!_immutable.default.List.isList(list)) {
return state;
}
const value = list.get(oldPosition);
list = list.delete(oldPosition);
list = list.insert(newPosition, value);
const updatedData = (0, _recordDataHelpers.deepSet)(data, listPath, list);
return setCurrentData(state, csid, updatedData);
};
const setFieldValue = (state, action) => {
const {
csid,
path
} = action.meta;
const data = getCurrentData(state, csid);
if (!data) {
return state;
}
const newValue = action.payload;
const updatedData = (0, _recordDataHelpers.deepSet)(data, path, newValue);
const nextState = setCurrentData(state, csid, updatedData);
return nextState;
};
const handleFieldComputeFulfilled = (state, action) => {
const {
csid,
path
} = action.meta;
const data = getCurrentData(state, csid);
if (!data) {
return state;
}
if (path.length === 0) {
// The entire record was computed.
const computedData = action.payload;
const updatedData = data.mergeDeep(computedData);
const nextState = setCurrentData(state, csid, updatedData);
return nextState;
}
// TODO: Handle an individual field being computed.
return state;
};
const handleRecordReadFulfilled = (state, action) => {
const {
csid,
recordTypeConfig
} = action.meta;
const data = (0, _recordDataHelpers.normalizeRecordData)(recordTypeConfig, _immutable.default.fromJS(action.payload.data));
let nextState = state.deleteIn([csid, 'isReadPending']).deleteIn([csid, 'error']);
nextState = setBaselineData(nextState, csid, data);
nextState = setCurrentData(nextState, csid, data);
return nextState;
};
const handleRecordSaveFulfilled = (state, action) => {
const {
csid,
recordTypeConfig,
relatedSubjectCsid,
recordPagePrimaryCsid
} = action.meta;
const data = (0, _recordDataHelpers.normalizeRecordData)(recordTypeConfig, _immutable.default.fromJS(action.payload.data));
let nextState = state;
nextState = nextState.deleteIn([csid, 'isSavePending']);
nextState = setBaselineData(nextState, csid, data);
nextState = setCurrentData(nextState, csid, data);
if (relatedSubjectCsid) {
nextState = nextState.setIn([relatedSubjectCsid, 'relationUpdatedTime'], (0, _recordDataHelpers.getUpdatedTimestamp)(data));
}
// Remove all record state besides the record that was just saved, any of its subrecords, and any
// related record. This isn't strictly necessary, but it's a good time to expire record data,
// since other records may have fields computed from this record via service layer handlers.
let persistCsids = [csid, relatedSubjectCsid, BASE_NEW_RECORD_KEY // Don't clear unsaved record data
];
const subrecord = nextState.getIn([csid, 'subrecord']);
if (subrecord) {
subrecord.valueSeq().forEach(subrecordCsid => {
persistCsids.push(subrecordCsid);
});
}
// avoid clearing any subrecords for the current page
const recordPageSubrecord = nextState.getIn([recordPagePrimaryCsid, 'subrecord']);
if (recordPageSubrecord) {
recordPageSubrecord.valueSeq().forEach(subrecordCsid => {
persistCsids.push(subrecordCsid);
});
}
persistCsids = new Set(persistCsids.filter(value => value !== null && typeof value !== 'undefined'));
nextState = clearFiltered(nextState, (recordState, candidateCsid) => !persistCsids.has(candidateCsid) && !candidateCsid.startsWith(`${BASE_NEW_RECORD_KEY}/`) // Don't clear unsaved subrecord data
&& !recordState.get('isSavePending') // Don't clear records that are being saved
&& candidateCsid !== recordPagePrimaryCsid // Don't clear the primary record data
);
return nextState;
};
const revertRecord = (state, action) => {
const {
recordTypeConfig,
csid
} = action.meta;
const baselineData = getBaselineData(state, csid);
let nextState = setCurrentData(state, csid, baselineData);
// Revert subrecords.
const subrecords = nextState.getIn([csid, 'subrecord']);
if (subrecords) {
subrecords.forEach(subrecordCsid => {
nextState = revertRecord(nextState, {
meta: {
csid: subrecordCsid
}
});
});
}
// Revert any cached subrecord csids that originated from fields in the record.
const configuredSubrecords = (0, _get.default)(recordTypeConfig, 'subrecords');
if (configuredSubrecords) {
Object.entries(configuredSubrecords).forEach(entry => {
const [subrecordName, subrecordConfig] = entry;
const {
csidField
} = subrecordConfig;
if (csidField) {
const revertedSubrecordCsid = (0, _recordDataHelpers.deepGet)(baselineData, csidField);
nextState = nextState.setIn([csid, 'subrecord', subrecordName], revertedSubrecordCsid);
// Revert the reattached subrecord.
nextState = revertRecord(nextState, {
meta: {
csid: revertedSubrecordCsid
}
});
}
});
}
return nextState;
};
const handleSubrecordCreated = (state, action) => {
const {
csid,
csidField,
subrecordName,
subrecordCsid,
isDefault
} = action.meta;
let nextState = state.setIn([csid, 'subrecord', subrecordName], subrecordCsid);
if (csidField) {
const currentData = getCurrentData(state, csid);
const baselineData = getBaselineData(state, csid);
if (isDefault && currentData === baselineData) {
// This subrecord was created as the default for the container. Set the csid field in both
// the baseline and current data, so it won't be considered a modification.
const updatedData = (0, _recordDataHelpers.deepSet)(baselineData, csidField, subrecordCsid);
nextState = setBaselineData(nextState, csid, updatedData);
nextState = setCurrentData(nextState, csid, updatedData);
} else {
const updatedData = (0, _recordDataHelpers.deepSet)(currentData, csidField, subrecordCsid);
nextState = setCurrentData(nextState, csid, updatedData);
}
}
return nextState;
};
const createNewSubrecord = (state, action) => {
const {
config,
csid,
csidField,
subrecordName,
subrecordTypeConfig,
cloneCsid,
isDefault,
stickyFields
} = action.meta;
let nextState = doCreateNew(state, config, subrecordTypeConfig, {
cloneCsid,
subrecordName,
stickyFields
});
const subrecordCsid = unsavedRecordKey(subrecordName);
nextState = handleSubrecordCreated(nextState, {
meta: {
csid,
csidField,
subrecordName,
subrecordCsid,
isDefault
}
});
return nextState;
};
const handleSubjectRelationsUpdated = (state, action) => {
// Currently relations are only ever created (not updated), and we don't bother to retrieve
// the relation record after creation. Technically we should retrieve the new relation
// record and use its updatedAt value, but that's an extra request. For now the action creator
// sets the updated time as a meta field.
const {
subject,
updatedTime
} = action.meta;
const subjectCsid = subject.csid;
if (state.has(subjectCsid)) {
return state.setIn([subjectCsid, 'relationUpdatedTime'], updatedTime);
}
return state;
};
const handleCreateIDFulfilled = (state, action) => {
const {
csid,
path,
transform
} = action.meta;
const data = getCurrentData(state, csid);
if (!data) {
return state;
}
const value = action.payload.data;
const createdID = typeof value === 'number' ? value.toString() : value;
const id = transform ? transform(createdID) : createdID;
const updatedData = (0, _recordDataHelpers.deepSet)(data, path, id);
const nextState = setCurrentData(state, csid, updatedData);
return nextState;
};
const handleValidationFailed = (state, action) => {
const errors = action.payload;
const {
csid,
path
} = action.meta;
return state.setIn([csid, 'validation', ...path], errors);
};
const handleValidationPassed = (state, action) => {
const {
csid,
path
} = action.meta;
return state.deleteIn([csid, 'validation', ...path]);
};
const handleTransitionFulfilled = (state, action) => {
const {
csid,
transitionName,
recordPagePrimaryCsid,
recordTypeConfig,
relatedSubjectCsid,
updatedTimestamp
} = action.meta;
let nextState = state.deleteIn([csid, 'isSavePending']);
if (transitionName === 'delete') {
nextState = nextState.delete(csid);
// Take this opportunity to expire other record data. This isn't strictly necessary, but it's a
// good time, since the deletion of a record may affect other records via service layer
// handlers.
nextState = clearFiltered(nextState, (recordState, candidateCsid) => !recordState.get('isSavePending') // Don't clear records that are being saved
&& candidateCsid !== recordPagePrimaryCsid // Don't clear the primary record data
);
} else {
const newData = (0, _get.default)(action, ['payload', 'data']);
if (newData) {
const data = (0, _recordDataHelpers.normalizeRecordData)(recordTypeConfig, _immutable.default.fromJS(newData));
nextState = setBaselineData(nextState, csid, data);
nextState = setCurrentData(nextState, csid, data);
}
}
if (relatedSubjectCsid) {
nextState = nextState.setIn([relatedSubjectCsid, 'relationUpdatedTime'], updatedTimestamp);
}
return nextState;
};
const handleDeleteFulfilled = (state, action) => {
const {
csid,
relatedSubjectCsid,
updatedTimestamp
} = action.meta;
let nextState = state.delete(csid);
if (relatedSubjectCsid) {
nextState = nextState.setIn([relatedSubjectCsid, 'relationUpdatedTime'], updatedTimestamp);
}
return nextState;
};
const detachSubrecord = (state, action) => createNewSubrecord(state, action);
const handleLoginFulfilled = (state, action) => {
const {
prevUsername,
username
} = action.meta;
if (prevUsername !== username) {
// The logged in user has changed. Remove all record state, because the new user may not be
// permitted to read some records that the previous user could.
return clearAll(state);
}
return state;
};
const getRefMap = data => {
let items = (0, _get.default)(data, ['ns2:abstract-common-list', 'list-item']) || [];
if (!Array.isArray(items)) {
items = [items];
}
const refMap = {};
items.forEach(({
csid,
referenced
}) => {
refMap[csid] = referenced;
});
return refMap;
};
const updateItemRefStates = (data, refMap) => {
let existingItems = data && data.getIn(['document', 'ns2:abstract-common-list', 'list-item']);
if (!existingItems) {
return data;
}
if (!_immutable.default.List.isList(existingItems)) {
existingItems = _immutable.default.List.of(existingItems);
}
const updatedItems = existingItems.map(item => item.set('referenced', refMap[item.get('csid')]));
return data.setIn(['document', 'ns2:abstract-common-list', 'list-item'], updatedItems);
};
const handleReadVocabularyItemRefsFulfilled = (state, action) => {
const {
csid
} = action.meta;
let nextState = state.deleteIn([csid, 'isReadVocabularyItemRefsPending']);
const refMap = getRefMap(action.payload.data);
const baselineData = getBaselineData(state, csid);
const currentData = getCurrentData(state, csid);
let nextBaselineData;
let nextCurrentData;
if (baselineData === currentData) {
nextBaselineData = updateItemRefStates(baselineData, refMap);
nextCurrentData = nextBaselineData;
} else {
nextBaselineData = updateItemRefStates(baselineData, refMap);
nextCurrentData = updateItemRefStates(currentData, refMap);
}
nextState = setBaselineData(nextState, csid, nextBaselineData);
nextState = setCurrentData(nextState, csid, nextCurrentData);
return nextState;
};
var _default = (state = _immutable.default.Map(), action) => {
switch (action.type) {
case _actionCodes.VALIDATION_FAILED:
return handleValidationFailed(state, action);
case _actionCodes.VALIDATION_PASSED:
return handleValidationPassed(state, action);
case _actionCodes.ADD_FIELD_INSTANCE:
return addFieldInstance(state, action);
case _actionCodes.CREATE_NEW_RECORD:
return createNewRecord(state, action);
case _actionCodes.CREATE_NEW_SUBRECORD:
return createNewSubrecord(state, action);
case _actionCodes.DELETE_FIELD_VALUE:
return deleteFieldValue(state, action);
case _actionCodes.MOVE_FIELD_VALUE:
return moveFieldValue(state, action);
case _actionCodes.SET_FIELD_VALUE:
return setFieldValue(state, action);
case _actionCodes.FIELD_COMPUTE_FULFILLED:
return handleFieldComputeFulfilled(state, action);
case _actionCodes.RECORD_CREATED:
return state.set(action.meta.newRecordCsid, state.get(action.meta.currentCsid)).delete(action.meta.currentCsid);
case _actionCodes.RECORD_READ_STARTED:
return state.setIn([action.meta.csid, 'isReadPending'], true);
case _actionCodes.RECORD_READ_FULFILLED:
return handleRecordReadFulfilled(state, action);
case _actionCodes.RECORD_READ_REJECTED:
return state.setIn([action.meta.csid, 'error'], _immutable.default.fromJS(action.payload)).deleteIn([action.meta.csid, 'isReadPending']);
case _actionCodes.RECORD_SAVE_STARTED:
return state.setIn([action.meta.csid, 'isSavePending'], true);
case _actionCodes.RECORD_SAVE_FULFILLED:
return handleRecordSaveFulfilled(state, action);
case _actionCodes.RECORD_SAVE_REJECTED:
return state
// I don't think there's any reason to store the save error.
// .setIn([action.meta.csid, 'error'], Immutable.fromJS(action.payload))
.deleteIn([action.meta.csid, 'isSavePending']);
case _actionCodes.RECORD_TRANSITION_STARTED:
return state.setIn([action.meta.csid, 'isSavePending'], true);
case _actionCodes.RECORD_TRANSITION_FULFILLED:
return handleTransitionFulfilled(state, action);
case _actionCodes.RECORD_TRANSITION_REJECTED:
return state.deleteIn([action.meta.csid, 'isSavePending']);
case _actionCodes.RECORD_DELETE_STARTED:
return state.setIn([action.meta.csid, 'isSavePending'], true);
case _actionCodes.RECORD_DELETE_FULFILLED:
return handleDeleteFulfilled(state, action);
case _actionCodes.RECORD_DELETE_REJECTED:
return state.deleteIn([action.meta.csid, 'isSavePending']);
case _actionCodes.SUBRECORD_CREATED:
return handleSubrecordCreated(state, action);
case _actionCodes.SUBRECORD_READ_FULFILLED:
return state.setIn([action.meta.csid, 'subrecord', action.meta.subrecordName], action.meta.subrecordCsid);
case _actionCodes.REVERT_RECORD:
return revertRecord(state, action);
case _actionCodes.SUBJECT_RELATIONS_UPDATED:
return handleSubjectRelationsUpdated(state, action);
case _actionCodes.CREATE_ID_FULFILLED:
return handleCreateIDFulfilled(state, action);
case _actionCodes.DETACH_SUBRECORD:
return detachSubrecord(state, action);
case _actionCodes.CLEAR_RECORD:
return clear(state, action.meta.csid, action.meta.clearSubrecords);
case _actionCodes.LOGIN_FULFILLED:
return handleLoginFulfilled(state, action);
case _actionCodes.LOGOUT_FULFILLED:
return clearAll(state);
case _actionCodes.SORT_FIELD_INSTANCES:
return sortFieldInstances(state, action);
case _actionCodes.READ_VOCABULARY_ITEM_REFS_STARTED:
return state.setIn([action.meta.csid, 'isReadVocabularyItemRefsPending'], true);
case _actionCodes.READ_VOCABULARY_ITEM_REFS_FULFILLED:
return handleReadVocabularyItemRefsFulfilled(state, action);
case _actionCodes.READ_VOCABULARY_ITEM_REFS_REJECTED:
return state.setIn([action.meta.csid, 'error'], _immutable.default.fromJS(action.payload)).deleteIn([action.meta.csid, 'isReadVocabularyItemRefsPending']);
default:
return state;
}
};
exports.default = _default;
const getData = (state, csid) => getCurrentData(state, csid);
exports.getData = getData;
const getError = (state, csid) => state.getIn([csid, 'error']);
exports.getError = getError;
const getSubrecordCsid = (state, csid, subrecordName) => state.getIn([csid, 'subrecord', subrecordName]);
exports.getSubrecordCsid = getSubrecordCsid;
let subrecordDataMemo = null;
const getSubrecordData = (state, csid) => {
const subrecords = state.getIn([csid, 'subrecord']);
let subrecordData = null;
if (subrecords) {
subrecordData = subrecords.map(subrecordCsid => getData(state, subrecordCsid));
if (_immutable.default.is(subrecordDataMemo, subrecordData)) {
return subrecordDataMemo;
}
}
subrecordDataMemo = subrecordData;
return subrecordData;
};
exports.getSubrecordData = getSubrecordData;
const getRelationUpdatedTimestamp = (state, csid) => state.getIn([csid, 'relationUpdatedTime']);
exports.getRelationUpdatedTimestamp = getRelationUpdatedTimestamp;
const getNewData = state => getData(state, unsavedRecordKey());
exports.getNewData = getNewData;
const getNewSubrecordCsid = (state, subrecordName) => getSubrecordCsid(state, unsavedRecordKey(), subrecordName);
exports.getNewSubrecordCsid = getNewSubrecordCsid;
const getValidationErrors = (state, csid) => state.getIn([csid, 'validation']);
exports.getValidationErrors = getValidationErrors;
const isReadPending = (state, csid) => state.getIn([csid, 'isReadPending']);
exports.isReadPending = isReadPending;
const isSavePending = (state, csid) => state.getIn([csid, 'isSavePending']);
exports.isSavePending = isSavePending;
const isReadVocabularyItemRefsPending = (state, csid) => state.getIn([csid, 'isReadVocabularyItemRefsPending']);
exports.isReadVocabularyItemRefsPending = isReadVocabularyItemRefsPending;
const isModified = (state, csid) => {
// Do a reference equality test between the current and baseline data. This will not detect if
// a change is made, then another change is made that undoes the first. But it's more efficient
// than a deep value equality test.
if (getCurrentData(state, csid) !== getBaselineData(state, csid)) {
return true;
}
// Check subrecords.
const subrecords = state.getIn([csid, 'subrecord']);
if (subrecords && subrecords.find(subrecordCsid => isModified(state, subrecordCsid))) {
return true;
}
return false;
};
exports.isModified = isModified;
const isModifiedExceptPart = (state, csid, exceptPart) => {
// Check subrecords.
const subrecords = state.getIn([csid, 'subrecord']);
if (subrecords && subrecords.find(subrecordCsid => isModified(state, subrecordCsid))) {
return true;
}
// Check parts, except for the given exception.
const data = state.getIn([csid, 'data']);
if (!data) {
return false;
}
const baselineData = data.get('baseline');
const currentData = data.get('current');
if (currentData === baselineData) {
return false;
}
const baselineDocument = baselineData.get('document');
const currentDocument = currentData.get('document');
if (currentDocument === baselineDocument) {
return false;
}
const modifiedPart = currentDocument.keySeq().filter(part => part !== exceptPart && !part.startsWith('@')).find(part => currentDocument.get(part) !== baselineDocument.get(part));
return !!modifiedPart;
};
exports.isModifiedExceptPart = isModifiedExceptPart;