From 27276a51b66767e434c9f8537b10a14f0cc31155 Mon Sep 17 00:00:00 2001 From: tejaede Date: Fri, 20 Feb 2026 15:55:21 -0600 Subject: [PATCH 1/4] ... --- data/service/raw-data-service.js | 57 ++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/data/service/raw-data-service.js b/data/service/raw-data-service.js index 35cd5ea00..842ec43c4 100644 --- a/data/service/raw-data-service.js +++ b/data/service/raw-data-service.js @@ -1,3 +1,5 @@ +const { last } = require("../../core/frb/operators"); + var DataService = require("./data-service").DataService, assign = require("../../../core/frb/assign"), compile = require("../../core/frb/compile-evaluator"), @@ -1552,6 +1554,7 @@ RawDataService.addClassProperties({ var mapping = this.mappingForType(type); if(mapping && mapping.rawDataPrimaryKeyCompiledExpressions) { throw "-dataIdentifierForTypeRawData(): Primary key missing for type '"+type.name+", rawData "+JSON.stringify(rawData); + // console.error("-dataIdentifierForTypeRawData(): Primary key missing for type '"+type.name+", rawData "+JSON.stringify(rawData)) } } } @@ -1662,7 +1665,9 @@ RawDataService.addClassProperties({ for (i = 0, countI = rawDataKeys.length; (i < countI); i++) { iUpdatedRawDataValue = rawData[rawDataKeys[i]]; - if (isFromUpdate && iUpdatedRawDataValue && ((iHasAddedValues = iUpdatedRawDataValue.hasOwnProperty("addedValues")) || (iHasRemovedValues = iUpdatedRawDataValue.hasOwnProperty("removedValues")))) { + iHasAddedValues = iUpdatedRawDataValue && iUpdatedRawDataValue.hasOwnProperty("addedValues"); + iHasRemovedValues = iUpdatedRawDataValue && iUpdatedRawDataValue.hasOwnProperty("removedValues"); + if (isFromUpdate && (iHasAddedValues || iHasRemovedValues)) { canRemoveRawDataKey = true; iCurrentRawDataValue = snapshot[rawDataKeys[i]]; @@ -1759,6 +1764,13 @@ RawDataService.addClassProperties({ } } + if (dataIdentifier.objectDescriptor.name === "Person") { + console.log("Person.updateSnapshot", { + rawData: Object.assign({}, rawData), + snapshot: Object.assign({}, snapshot) + }); + } + return rawData; } } @@ -1776,6 +1788,18 @@ RawDataService.addClassProperties({ } }, + /** + * Removes the pending snapshot of the values of record for the DataIdentifier argument + * + * @private + * @argument {DataIdentifier} dataIdentifier + */ + removePendingSnapshot: { + value: function (dataIdentifier) { + this._pendingSnapshot.delete(dataIdentifier); + } + }, + /** * Returns the snapshot associated with the DataIdentifier argument if available * @@ -1845,14 +1869,19 @@ RawDataService.addClassProperties({ pendingSnapshotForDataIdentifier: { value: function (dataIdentifier) { let pendingSnapshot = this._pendingSnapshot.get(dataIdentifier); + let tmpPendingSnapshot; if(!pendingSnapshot) { let snapshot = this.snapshotForDataIdentifier(dataIdentifier); if(snapshot) { + tmpPendingSnapshot = Object.assign({}, snapshot); pendingSnapshot = Object.create(snapshot); this._pendingSnapshot.set(dataIdentifier, pendingSnapshot); } } + if (dataIdentifier.objectDescriptor.name === "Person" && pendingSnapshot) { + console.log("Person.pendingSnapshotForDataIdentifier", Object.assign({}, pendingSnapshot), tmpPendingSnapshot); + } return pendingSnapshot; } }, @@ -4427,6 +4456,13 @@ RawDataService.addClassProperties({ // //when saved, but what if save fails and changes happen in-between? // operationData[aRawProperty] = aPropertyChanges; + if (object.objectDescriptor.name === "Person") { + console.log("Person.processObjectChanges", { + lastReadSnapshot: Object.assign({}, lastReadSnapshot), + rawDataSnapshot: Object.assign({}, rawDataSnapshot) + }); + } + return this._mapObjectPropertyToRawData(object, aProperty, operationData, undefined/*context*/, aPropertyChanges.addedValues, aPropertyChanges.removedValues, lastReadSnapshot, rawDataSnapshot); } @@ -4650,6 +4686,16 @@ RawDataService.addClassProperties({ console.warn("pendingSnapshotForDataIdentifier: NO SNAPSHOT FOUND FOR "+dataIdentifier); } + if (object.objectDescriptor.name === "Person") { + console.log("Person.makeSaveOperation", { + pendingSnapshotLocked: snapshot && Object.assign({}, snapshot), + pendingSnapshot: snapshot, + operationData: operationData, + dataSnapshotPassedToOperation: dataSnapshot, + criteria: criteria && Object.assign({}, criteria) + }); + } + if (localizableProperties && localizableProperties.size) { operation.locales = this.localesForObject(object) } @@ -4958,15 +5004,22 @@ RawDataService.addClassProperties({ self.rootService.registerUniqueObjectWithDataIdentifier(iObject, iDataIdentifier); } self.recordSnapshot(iDataIdentifier, iOperation.data); - + self.removePendingSnapshot(iDataIdentifier); } else if (iOperation.type === UpdateOperation || iOperation.type === NoOpOperation) { iDataIdentifier = self.dataIdentifierForObject(iObject); + if (iDataIdentifier.objectDescriptor.name === "Person") { + console.log("Person.recordSnapshotAfterSave", { + data: iOperation.data + }); + } self.recordSnapshot(iDataIdentifier, iOperation.data, true); + self.removePendingSnapshot(iDataIdentifier); } else if (iOperation.type === DeleteOperation) { iDataIdentifier = self.dataIdentifierForObject(iObject); //Removes the snapshot we have for iDataIdentifier self.removeSnapshot(iDataIdentifier); + self.removePendingSnapshot(iDataIdentifier); } } From b147f24e3204e4900bccc7c8ba7e8d2cab9ffa73 Mon Sep 17 00:00:00 2001 From: tejaede Date: Mon, 23 Feb 2026 21:34:32 -0600 Subject: [PATCH 2/4] Preferred Next Role fix w/ sparse array + logging --- .../raw-foreign-value-to-object-converter.js | 6 ++- data/service/data-service.js | 12 +++++- data/service/expression-data-mapping.js | 19 ++++++---- data/service/raw-data-service.js | 38 +++++++++++++------ 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/data/converter/raw-foreign-value-to-object-converter.js b/data/converter/raw-foreign-value-to-object-converter.js index 12c43ea69..7c60e0e4e 100644 --- a/data/converter/raw-foreign-value-to-object-converter.js +++ b/data/converter/raw-foreign-value-to-object-converter.js @@ -877,10 +877,14 @@ exports.RawForeignValueToObjectConverter = RawValueToObjectConverter.specialize( //forEach skipps over holes of a sparse array v.forEach(function(value) { /* - Make sure we have a valid data object anf not null nor undefined before trying to get their primary key + Make sure we have a valid data object anf not null nor undefined before trying to get their primary key */ if(value) { result.push(service.dataIdentifierForObject(value).primaryKey); + } else { + //TODO: Is this safe? It was added because, in some cases, you may want to preserve a sparse array. E.g. The + //array has a fixed length, but not all values are defined + result.push(value); } }); currentRule = null; diff --git a/data/service/data-service.js b/data/service/data-service.js index 55c3c9170..91b524f6f 100644 --- a/data/service/data-service.js +++ b/data/service/data-service.js @@ -4550,6 +4550,10 @@ DataService.addClassProperties( //inversePropertyDescriptor, self = this; + if (dataObject.objectDescriptor.name === "Person" && (addedValues || removedValues)) { + console.log("DataService._registerDataObjectChangesFromEvent", key, addedValues, removedValues); + } + /* Benoit refactoring saveChanges: shouldn't we be able to know that if there are no changesForDataObject, as we create on, it would ve the only time we'd have to call: @@ -4661,6 +4665,7 @@ DataService.addClassProperties( if (!manyChanges) { manyChanges = {}; + manyChanges.index = changeEvent.index; changesForDataObject.set(key, manyChanges); } @@ -4692,7 +4697,8 @@ DataService.addClassProperties( var registeredRemovedValues = manyChanges.removedValues; if (!registeredRemovedValues) { if (!isDataObjectBeingMapped) { - manyChanges.removedValues = registeredRemovedValues = new Set(removedValues); + manyChanges.removedValues = removedValues; + manyChanges.removedValuesSet = registeredRemovedValues = new Set(removedValues); } self._removeDataObjectPropertyDescriptorValuesForInversePropertyDescriptor( dataObject, @@ -4752,11 +4758,13 @@ DataService.addClassProperties( */ if (Array.isArray(manyChanges) && manyChanges.equals(addedValues)) { manyChanges = {}; + manyChanges.index = changeEvent.index; changesForDataObject.set(key, manyChanges); } if (!isDataObjectBeingMapped) { - manyChanges.addedValues = registeredAddedValues = new Set(addedValues); + manyChanges.addedValues = addedValues; + manyChanges.addedValuesSet = registeredAddedValues = new Set(addedValues); } self._addDataObjectPropertyDescriptorValuesForInversePropertyDescriptor( dataObject, diff --git a/data/service/expression-data-mapping.js b/data/service/expression-data-mapping.js index ec0f29a7d..03562619d 100644 --- a/data/service/expression-data-mapping.js +++ b/data/service/expression-data-mapping.js @@ -1851,6 +1851,9 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData if (this._isAsync(result)) { self = this; result.then(function (value) { + propertyName; + scope; + rule; self._setRawDataPropertyValueIfNeeded(rawData, propertyName, value, lastReadSnapshot, rawDataSnapshot); // rawData[propertyName] = value; return null; @@ -2011,7 +2014,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData _mapObjectPropertyToRawDataProperty: { value: function(object, propertyName, data, rawPropertyName, added, removed, _rule, lastReadSnapshot, rawDataSnapshot) { - if((added && added.size > 0) || (removed && removed.size > 0 )) { + if((added && added.length > 0) || (removed && removed.length > 0 )) { var tmpExtendObject, //We derived object so we can pretend the value of the property is alternatively added, then removed, to get the mapping done. //tmpExtendObject = Object.create(object), @@ -2026,7 +2029,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData data[rawPropertyName] = aPropertyChanges; - if(added && added.size > 0) { + if(added && added.length > 0) { /* Here we have a situation where in the most common case object[propertyName] is not equal to the content of added, like if there were pre-exising values. @@ -2038,7 +2041,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData tmpExtendObject = {}; for(i=0, countI = requirements.length; ( i 0 ) { + if(removed && removed.length > 0 ) { requirements = (requirements || _rule.requirements); // tmpExtendObject[propertyName] = Array.from(result); @@ -2062,7 +2065,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData for(i=0, countI = requirements.length; ( i { + let mapping = this.mappingForObject(object), + rawRules = mapping.rawDataMappingRulesForObjectPropertyName(aProperty), + rawDataProperty; + + if(rawRules) { + var iterator = rawRules.values(); + + while((rule = iterator.next().value)) { + rawDataProperty = rule.targetPath; + if (operationData[rawDataProperty]) { + operationData[rawDataProperty].index = aPropertyChanges.index; + } + if (operationData[rawDataProperty] && operationData[rawDataProperty].removedValues) { + operationData[rawDataProperty].removedValues.index = aPropertyChanges.index; + } + } + } + + return output; + }) + } else { + return result; } - return this._mapObjectPropertyToRawData(object, aProperty, operationData, undefined/*context*/, aPropertyChanges.addedValues, aPropertyChanges.removedValues, lastReadSnapshot, rawDataSnapshot); + // return this._mapObjectPropertyToRawData(object, aProperty, operationData, undefined/*context*/, aPropertyChanges.addedValues, aPropertyChanges.removedValues, lastReadSnapshot, rawDataSnapshot); } else { @@ -5007,11 +5028,6 @@ RawDataService.addClassProperties({ self.removePendingSnapshot(iDataIdentifier); } else if (iOperation.type === UpdateOperation || iOperation.type === NoOpOperation) { iDataIdentifier = self.dataIdentifierForObject(iObject); - if (iDataIdentifier.objectDescriptor.name === "Person") { - console.log("Person.recordSnapshotAfterSave", { - data: iOperation.data - }); - } self.recordSnapshot(iDataIdentifier, iOperation.data, true); self.removePendingSnapshot(iDataIdentifier); } else if (iOperation.type === DeleteOperation) { From cc828c8704bdae2542b18a1aa27fbedb9529fdef Mon Sep 17 00:00:00 2001 From: tejaede Date: Mon, 23 Feb 2026 21:41:05 -0600 Subject: [PATCH 3/4] Remove logging --- data/service/data-service.js | 4 ---- data/service/raw-data-service.js | 20 -------------------- 2 files changed, 24 deletions(-) diff --git a/data/service/data-service.js b/data/service/data-service.js index 91b524f6f..14b7efcde 100644 --- a/data/service/data-service.js +++ b/data/service/data-service.js @@ -4550,10 +4550,6 @@ DataService.addClassProperties( //inversePropertyDescriptor, self = this; - if (dataObject.objectDescriptor.name === "Person" && (addedValues || removedValues)) { - console.log("DataService._registerDataObjectChangesFromEvent", key, addedValues, removedValues); - } - /* Benoit refactoring saveChanges: shouldn't we be able to know that if there are no changesForDataObject, as we create on, it would ve the only time we'd have to call: diff --git a/data/service/raw-data-service.js b/data/service/raw-data-service.js index f7f3a042b..064ab4b09 100644 --- a/data/service/raw-data-service.js +++ b/data/service/raw-data-service.js @@ -1764,13 +1764,6 @@ RawDataService.addClassProperties({ } } - if (dataIdentifier.objectDescriptor.name === "Person") { - console.log("Person.updateSnapshot", { - rawData: Object.assign({}, rawData), - snapshot: Object.assign({}, snapshot) - }); - } - return rawData; } } @@ -1879,9 +1872,6 @@ RawDataService.addClassProperties({ this._pendingSnapshot.set(dataIdentifier, pendingSnapshot); } } - if (dataIdentifier.objectDescriptor.name === "Person" && pendingSnapshot) { - console.log("Person.pendingSnapshotForDataIdentifier", Object.assign({}, pendingSnapshot), tmpPendingSnapshot); - } return pendingSnapshot; } }, @@ -4707,16 +4697,6 @@ RawDataService.addClassProperties({ console.warn("pendingSnapshotForDataIdentifier: NO SNAPSHOT FOUND FOR "+dataIdentifier); } - if (object.objectDescriptor.name === "Person") { - console.log("Person.makeSaveOperation", { - pendingSnapshotLocked: snapshot && Object.assign({}, snapshot), - pendingSnapshot: snapshot, - operationData: operationData, - dataSnapshotPassedToOperation: dataSnapshot, - criteria: criteria && Object.assign({}, criteria) - }); - } - if (localizableProperties && localizableProperties.size) { operation.locales = this.localesForObject(object) } From 51d96b86224de6548cc56c9d91c13aa336d0be50 Mon Sep 17 00:00:00 2001 From: tejaede Date: Tue, 24 Feb 2026 16:59:39 -0600 Subject: [PATCH 4/4] ... --- data/service/data-operation.js | 2 +- data/service/expression-data-mapping.js | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/data/service/data-operation.js b/data/service/data-operation.js index d09654f73..6fc8f5345 100644 --- a/data/service/data-operation.js +++ b/data/service/data-operation.js @@ -201,7 +201,7 @@ var Montage = require("../../core/core").Montage, exports.DataOperationType = DataOperationType = new Enum().initWithMembersAndValues(dataOperationTypes,dataOperationTypes); -var dataOperationErrorNames = ["DatabaseMissing", "ObjectDescriptorStoreMissing", "PropertyDescriptorStoreMissing", "InvalidInput", "SyntaxError", "PropertyDescriptorNotFound", "PropertyMappingNotFound", "TransactionDeadlock"]; +var dataOperationErrorNames = ["DatabaseMissing", "ObjectDescriptorStoreMissing", "PropertyDescriptorStoreMissing", "InvalidInput", "SyntaxError", "PropertyDescriptorNotFound", "PropertyMappingNotFound", "TransactionDeadlock", "FunctionMissing"]; exports.DataOperationErrorNames = DataOperationErrorNames = new Enum().initWithMembersAndValues(dataOperationErrorNames,dataOperationErrorNames); // exports.DataOperationError.ObjectDescriptorStoreMissingError = Error.specialize({ diff --git a/data/service/expression-data-mapping.js b/data/service/expression-data-mapping.js index 03562619d..8ebc6b69a 100644 --- a/data/service/expression-data-mapping.js +++ b/data/service/expression-data-mapping.js @@ -1851,9 +1851,6 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData if (this._isAsync(result)) { self = this; result.then(function (value) { - propertyName; - scope; - rule; self._setRawDataPropertyValueIfNeeded(rawData, propertyName, value, lastReadSnapshot, rawDataSnapshot); // rawData[propertyName] = value; return null; @@ -2081,7 +2078,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData } if(addedResultIsPromise && removedResultIsPromise) { - return Promise.all([addedResultIsPromise, removedResultIsPromise]); + return Promise.all([addedResult, removedResult]); } else if(addedResultIsPromise) { return addedResult; } else if(removedResultIsPromise) {