@@ -14,7 +14,6 @@ import {
1414 ObjectsMapEntry ,
1515 ObjectsMapOp ,
1616 ObjectsMapSemantics ,
17- ObjectState ,
1817} from './objectmessage' ;
1918import { Objects } from './objects' ;
2019
@@ -42,9 +41,6 @@ export type LiveMapObjectData = ObjectIdObjectData | ValueObjectData;
4241
4342export interface LiveMapEntry {
4443 tombstone : boolean ;
45- /**
46- * Can't use serial from the operation that deleted the entry for the same reason as for {@link LiveObject} tombstones, see explanation there.
47- */
4844 tombstonedAt : number | undefined ;
4945 timeserial : string | undefined ;
5046 data : LiveMapObjectData | undefined ;
@@ -84,12 +80,9 @@ export class LiveMap<T extends API.LiveMapType> extends LiveObject<LiveMapData,
8480 *
8581 * @internal
8682 */
87- static fromObjectState < T extends API . LiveMapType > (
88- objects : Objects ,
89- objectState : ObjectState < ObjectData > ,
90- ) : LiveMap < T > {
91- const obj = new LiveMap < T > ( objects , objectState . map ?. semantics ! , objectState . objectId ) ;
92- obj . overrideWithObjectState ( objectState ) ;
83+ static fromObjectState < T extends API . LiveMapType > ( objects : Objects , objectMessage : ObjectMessage ) : LiveMap < T > {
84+ const obj = new LiveMap < T > ( objects , objectMessage . object ! . map ! . semantics ! , objectMessage . object ! . objectId ) ;
85+ obj . overrideWithObjectState ( objectMessage ) ;
9386 return obj ;
9487 }
9588
@@ -455,12 +448,12 @@ export class LiveMap<T extends API.LiveMapType> extends LiveObject<LiveMapData,
455448 // leave an explicit return here, so that TS knows that update object is always set after the switch statement.
456449 return ;
457450 } else {
458- update = this . _applyMapRemove ( op . mapOp , opSerial ) ;
451+ update = this . _applyMapRemove ( op . mapOp , opSerial , msg . serialTimestamp ) ;
459452 }
460453 break ;
461454
462455 case ObjectOperationAction . OBJECT_DELETE :
463- update = this . _applyObjectDelete ( ) ;
456+ update = this . _applyObjectDelete ( msg ) ;
464457 break ;
465458
466459 default :
@@ -478,7 +471,12 @@ export class LiveMap<T extends API.LiveMapType> extends LiveObject<LiveMapData,
478471 * @internal
479472 * @spec RTLM6
480473 */
481- overrideWithObjectState ( objectState : ObjectState < ObjectData > ) : LiveMapUpdate < T > | LiveObjectUpdateNoop {
474+ overrideWithObjectState ( objectMessage : ObjectMessage ) : LiveMapUpdate < T > | LiveObjectUpdateNoop {
475+ const objectState = objectMessage . object ;
476+ if ( objectState == null ) {
477+ throw new this . _client . ErrorInfo ( `Missing object state; LiveMap objectId=${ this . getObjectId ( ) } ` , 92000 , 500 ) ;
478+ }
479+
482480 if ( objectState . objectId !== this . getObjectId ( ) ) {
483481 throw new this . _client . ErrorInfo (
484482 `Invalid object state: object state objectId=${ objectState . objectId } ; LiveMap objectId=${ this . getObjectId ( ) } ` ,
@@ -534,7 +532,7 @@ export class LiveMap<T extends API.LiveMapType> extends LiveObject<LiveMapData,
534532 const previousDataRef = this . _dataRef ;
535533 if ( objectState . tombstone ) {
536534 // tombstone this object and ignore the data from the object state message
537- this . tombstone ( ) ;
535+ this . tombstone ( objectMessage ) ;
538536 } else {
539537 // override data for this object with data from the object state
540538 this . _createOperationIsMerged = false ; // RTLM6b
@@ -644,7 +642,7 @@ export class LiveMap<T extends API.LiveMapType> extends LiveObject<LiveMapData,
644642 let update : LiveMapUpdate < T > | LiveObjectUpdateNoop ;
645643 if ( entry . tombstone === true ) {
646644 // RTLM6d1b - entry in MAP_CREATE op is removed, try to apply MAP_REMOVE op
647- update = this . _applyMapRemove ( { key } , opSerial ) ;
645+ update = this . _applyMapRemove ( { key } , opSerial , entry . serialTimestamp ) ;
648646 } else {
649647 // RTLM6d1a - entry in MAP_CREATE op is not removed, try to set it via MAP_SET op
650648 update = this . _applyMapSet ( { key, data : entry . data } , opSerial ) ;
@@ -726,7 +724,7 @@ export class LiveMap<T extends API.LiveMapType> extends LiveObject<LiveMapData,
726724 Utils . isNil ( op . data . string ) )
727725 ) {
728726 throw new ErrorInfo (
729- `Invalid object data for MAP_SET op on objectId=${ this . getObjectId ( ) } on key=${ op . key } ` ,
727+ `Invalid object data for MAP_SET op on objectId=${ this . getObjectId ( ) } on key=" ${ op . key } " ` ,
730728 92000 ,
731729 500 ,
732730 ) ;
@@ -779,6 +777,7 @@ export class LiveMap<T extends API.LiveMapType> extends LiveObject<LiveMapData,
779777 private _applyMapRemove (
780778 op : ObjectsMapOp < ObjectData > ,
781779 opSerial : string | undefined ,
780+ opTimestamp : number | undefined ,
782781 ) : LiveMapUpdate < T > | LiveObjectUpdateNoop {
783782 const existingEntry = this . _dataRef . data . get ( op . key ) ;
784783 // RTLM8a
@@ -793,17 +792,30 @@ export class LiveMap<T extends API.LiveMapType> extends LiveObject<LiveMapData,
793792 return { noop : true } ;
794793 }
795794
795+ let tombstonedAt : number ;
796+ if ( opTimestamp != null ) {
797+ tombstonedAt = opTimestamp ;
798+ } else {
799+ this . _client . Logger . logAction (
800+ this . _client . logger ,
801+ this . _client . Logger . LOG_MINOR ,
802+ 'LiveMap._applyMapRemove()' ,
803+ `map key has been removed but no "serialTimestamp" found in the message, using local clock instead; key="${ op . key } ", objectId=${ this . getObjectId ( ) } ` ,
804+ ) ;
805+ tombstonedAt = Date . now ( ) ; // best-effort estimate since no timestamp provided by the server
806+ }
807+
796808 if ( existingEntry ) {
797809 // RTLM8a2
798810 existingEntry . tombstone = true ; // RTLM8a2c
799- existingEntry . tombstonedAt = Date . now ( ) ;
811+ existingEntry . tombstonedAt = tombstonedAt ;
800812 existingEntry . timeserial = opSerial ; // RTLM8a2b
801813 existingEntry . data = undefined ; // RTLM8a2a
802814 } else {
803815 // RTLM8b, RTLM8b1
804816 const newEntry : LiveMapEntry = {
805817 tombstone : true , // RTLM8b2
806- tombstonedAt : Date . now ( ) ,
818+ tombstonedAt : tombstonedAt ,
807819 timeserial : opSerial ,
808820 data : undefined ,
809821 } ;
@@ -869,12 +881,27 @@ export class LiveMap<T extends API.LiveMapType> extends LiveObject<LiveMapData,
869881 }
870882 }
871883
884+ let tombstonedAt : number | undefined ;
885+ if ( entry . tombstone === true ) {
886+ if ( entry . serialTimestamp != null ) {
887+ tombstonedAt = entry . serialTimestamp ;
888+ } else {
889+ this . _client . Logger . logAction (
890+ this . _client . logger ,
891+ this . _client . Logger . LOG_MINOR ,
892+ 'LiveMap._liveMapDataFromMapEntries()' ,
893+ `map key is removed but no "serialTimestamp" found, using local clock instead; key="${ key } ", objectId=${ this . getObjectId ( ) } ` ,
894+ ) ;
895+ tombstonedAt = Date . now ( ) ; // best-effort estimate since no timestamp provided by the server
896+ }
897+ }
898+
872899 const liveDataEntry : LiveMapEntry = {
873900 timeserial : entry . timeserial ,
874901 data : liveData ,
875902 // consider object as tombstoned only if we received an explicit flag stating that. otherwise it exists
876903 tombstone : entry . tombstone === true ,
877- tombstonedAt : entry . tombstone === true ? Date . now ( ) : undefined ,
904+ tombstonedAt,
878905 } ;
879906
880907 liveMapData . data . set ( key , liveDataEntry ) ;
0 commit comments