@fluid-experimental/property-dds 2.0.0-dev.2.2.0.111723 → 2.0.0-dev.3.1.0.125672

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,7 +30,11 @@ import {
30
30
  rebaseToRemoteChanges,
31
31
  } from "@fluid-experimental/property-changeset";
32
32
 
33
- import { PropertyFactory, BaseProperty, NodeProperty } from "@fluid-experimental/property-properties";
33
+ import {
34
+ PropertyFactory,
35
+ BaseProperty,
36
+ NodeProperty,
37
+ } from "@fluid-experimental/property-properties";
34
38
 
35
39
  import { v4 as uuidv4 } from "uuid";
36
40
  import axios from "axios";
@@ -73,6 +77,7 @@ export interface ISnapshotSummary {
73
77
  remoteTipView?: SerializedChangeSet;
74
78
  remoteChanges?: IPropertyTreeMessage[];
75
79
  unrebasedRemoteChanges?: Record<string, IRemotePropertyTreeMessage>;
80
+ remoteHeadGuid: string;
76
81
  }
77
82
 
78
83
  export interface SharedPropertyTreeOptions {
@@ -82,9 +87,11 @@ export interface SharedPropertyTreeOptions {
82
87
  }
83
88
 
84
89
  export interface ISharedPropertyTreeEncDec {
85
- messageEncoder: { encode: (IPropertyTreeMessage) => IPropertyTreeMessage;
86
- decode: (IPropertyTreeMessage) => IPropertyTreeMessage; };
87
- summaryEncoder: { encode: (ISnapshotSummary) => Buffer; decode: (Buffer) => ISnapshotSummary; };
90
+ messageEncoder: {
91
+ encode: (IPropertyTreeMessage) => IPropertyTreeMessage;
92
+ decode: (IPropertyTreeMessage) => IPropertyTreeMessage;
93
+ };
94
+ summaryEncoder: { encode: (ISnapshotSummary) => Buffer; decode: (Buffer) => ISnapshotSummary };
88
95
  }
89
96
 
90
97
  export interface IPropertyTreeConfig {
@@ -92,8 +99,10 @@ export interface IPropertyTreeConfig {
92
99
  }
93
100
 
94
101
  const defaultEncDec: ISharedPropertyTreeEncDec = {
95
- messageEncoder: { encode: (msg: IPropertyTreeMessage) => msg,
96
- decode: (msg: IPropertyTreeMessage) => msg },
102
+ messageEncoder: {
103
+ encode: (msg: IPropertyTreeMessage) => msg,
104
+ decode: (msg: IPropertyTreeMessage) => msg,
105
+ },
97
106
  summaryEncoder: {
98
107
  encode: (summary: ISnapshotSummary) => {
99
108
  const packr = new Packr();
@@ -184,7 +193,8 @@ export class SharedPropertyTree extends SharedObject {
184
193
  }
185
194
 
186
195
  private scopeFutureDeltasToPaths(paths?: string[]) {
187
- const socket = (this.runtime.deltaManager as any).deltaManager.connectionManager.connection.socket;
196
+ const socket = (this.runtime.deltaManager as any).deltaManager.connectionManager.connection
197
+ .socket;
188
198
  socket.emit("partial_checkout", { paths });
189
199
  }
190
200
 
@@ -192,15 +202,19 @@ export class SharedPropertyTree extends SharedObject {
192
202
  // Check whether anybody is listening. If not, we don't want to pay the price
193
203
  // for the serialization of the data structure
194
204
  if (this.listenerCount("localModification") > 0) {
195
- const changes = this._root._serialize(true, false, BaseProperty.MODIFIED_STATE_FLAGS.DIRTY);
196
- this._root.cleanDirty(BaseProperty.MODIFIED_STATE_FLAGS.DIRTY);
205
+ const changes = this._root._serialize(
206
+ true,
207
+ false,
208
+ BaseProperty.MODIFIED_STATE_FLAGS.DIRTY,
209
+ );
210
+ this._root.cleanDirty(BaseProperty.MODIFIED_STATE_FLAGS.DIRTY);
197
211
  const _changeSet = new ChangeSet(changes);
198
212
  if (!isEmpty(_changeSet.getSerializedChangeSet())) {
199
213
  this.emit("localModification", _changeSet);
200
214
  }
201
215
  } else {
202
- this._root.cleanDirty(BaseProperty.MODIFIED_STATE_FLAGS.DIRTY);
203
- }
216
+ this._root.cleanDirty(BaseProperty.MODIFIED_STATE_FLAGS.DIRTY);
217
+ }
204
218
  }
205
219
 
206
220
  public get changeSet(): SerializedChangeSet {
@@ -210,15 +224,19 @@ export class SharedPropertyTree extends SharedObject {
210
224
 
211
225
  public get activeCommit(): IPropertyTreeMessage {
212
226
  return this.localChanges.length > 0
213
- ? this.localChanges[this.localChanges.length - 1]
214
- : this.remoteChanges[this.remoteChanges.length - 1];
227
+ ? this.localChanges[this.localChanges.length - 1]
228
+ : this.remoteChanges[this.remoteChanges.length - 1];
215
229
  }
216
230
  public get root(): NodeProperty {
217
231
  return this._root as NodeProperty;
218
232
  }
219
233
 
220
234
  public commit(metadata?: Metadata, submitEmptyChange?: boolean) {
221
- const changes = this._root._serialize(true, false, BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE);
235
+ const changes = this._root._serialize(
236
+ true,
237
+ false,
238
+ BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE,
239
+ );
222
240
 
223
241
  let doSubmit = !!submitEmptyChange;
224
242
 
@@ -236,15 +254,15 @@ export class SharedPropertyTree extends SharedObject {
236
254
  /**
237
255
  * This method encodes the given message to the transfer form
238
256
  * @param change - The message to be encoded.
239
- */
257
+ */
240
258
  private encodeMessage(change: IPropertyTreeMessage): IPropertyTreeMessage {
241
259
  return this.propertyTreeConfig.encDec.messageEncoder.encode(change);
242
260
  }
243
261
 
244
262
  /**
245
- * This method decodes message from the transfer form.
263
+ * This method decodes message from the transfer form.
246
264
  * @param transferChange - The message to be decoded.
247
- */
265
+ */
248
266
  private decodeMessage(transferChange: IPropertyTreeMessage): IPropertyTreeMessage {
249
267
  return this.propertyTreeConfig.encDec.messageEncoder.decode(transferChange);
250
268
  }
@@ -253,18 +271,18 @@ export class SharedPropertyTree extends SharedObject {
253
271
  const _changeSet = new ChangeSet(changeSet);
254
272
  _changeSet._toReversibleChangeSet(this.tipView);
255
273
 
256
- const remoteHeadGuid =
257
- this.remoteChanges.length > 0
258
- ? this.remoteChanges[this.remoteChanges.length - 1].guid
259
- : this.headCommitGuid;
274
+ this.updateRemoteHeadGuid();
275
+
260
276
  const change = {
261
277
  op: OpKind.ChangeSet,
262
278
  changeSet,
263
279
  metadata,
264
280
  guid: uuidv4(),
265
- remoteHeadGuid,
281
+ remoteHeadGuid: this.headCommitGuid,
266
282
  referenceGuid:
267
- this.localChanges.length > 0 ? this.localChanges[this.localChanges.length - 1].guid : remoteHeadGuid,
283
+ this.localChanges.length > 0
284
+ ? this.localChanges[this.localChanges.length - 1].guid
285
+ : this.headCommitGuid,
268
286
  localBranchStart: this.localChanges.length > 0 ? this.localChanges[0].guid : undefined,
269
287
  useMH: this.useMH,
270
288
  };
@@ -321,10 +339,20 @@ export class SharedPropertyTree extends SharedObject {
321
339
  * @param localOpMetadata - For local client messages, this is the metadata that was submitted with the message.
322
340
  * For messages from a remote client, this will be undefined.
323
341
  */
324
- protected processCore(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown) {
325
- if (message.type === MessageType.Operation && message.sequenceNumber > this.skipSequenceNumber) {
342
+ protected processCore(
343
+ message: ISequencedDocumentMessage,
344
+ local: boolean,
345
+ localOpMetadata: unknown,
346
+ ) {
347
+ if (
348
+ message.type === MessageType.Operation &&
349
+ message.sequenceNumber > this.skipSequenceNumber
350
+ ) {
326
351
  const change: IPropertyTreeMessage = this.decodeMessage(cloneDeep(message.contents));
327
- const content: IRemotePropertyTreeMessage = { ...change, sequenceNumber: message.sequenceNumber };
352
+ const content: IRemotePropertyTreeMessage = {
353
+ ...change,
354
+ sequenceNumber: message.sequenceNumber,
355
+ };
328
356
  switch (content.op) {
329
357
  case OpKind.ChangeSet:
330
358
  // If the op originated locally from this client, we've already accounted for it
@@ -337,6 +365,18 @@ export class SharedPropertyTree extends SharedObject {
337
365
  }
338
366
  }
339
367
 
368
+ private addRemoteChange(change: IPropertyTreeMessage) {
369
+ this.remoteChanges.push(change);
370
+ this.updateRemoteHeadGuid();
371
+ }
372
+
373
+ private updateRemoteHeadGuid() {
374
+ this.headCommitGuid =
375
+ this.remoteChanges.length > 0
376
+ ? this.remoteChanges[this.remoteChanges.length - 1].guid
377
+ : this.headCommitGuid;
378
+ }
379
+
340
380
  public static prune(
341
381
  minimumSequenceNumber: number,
342
382
  remoteChanges: IPropertyTreeMessage[],
@@ -384,7 +424,10 @@ export class SharedPropertyTree extends SharedObject {
384
424
  visitedUnrebasedRemoteChanges.add(visitor.guid);
385
425
  }
386
426
  // if we exited the loop because we hit a remote change than add it as visited
387
- if (visitor.remoteHeadGuid === visitor.referenceGuid && remoteChangeMap.has(visitor.referenceGuid)) {
427
+ if (
428
+ visitor.remoteHeadGuid === visitor.referenceGuid &&
429
+ remoteChangeMap.has(visitor.referenceGuid)
430
+ ) {
388
431
  visitedRemoteChanges.add(visitor.referenceGuid);
389
432
  }
390
433
 
@@ -408,7 +451,9 @@ export class SharedPropertyTree extends SharedObject {
408
451
  }
409
452
 
410
453
  // find minimum index
411
- const minIndex = Math.min(...[...visitedRemoteChanges].map((key) => remoteChangeMap.get(key) as number));
454
+ const minIndex = Math.min(
455
+ ...[...visitedRemoteChanges].map((key) => remoteChangeMap.get(key) as number),
456
+ );
412
457
 
413
458
  const prunedRemoteChanges = remoteChanges.slice(minIndex);
414
459
  pruned += minIndex;
@@ -445,32 +490,32 @@ export class SharedPropertyTree extends SharedObject {
445
490
  * This method decodes the serialized form of the summary into the local summary (snapshot) object.
446
491
  * @param serializedSummary - The serialized summary representation.
447
492
  * @returns The local summary (snapshot)representation.
448
- */
493
+ */
449
494
  private decodeSummary(serializedSummary): ISnapshotSummary {
450
495
  return this.propertyTreeConfig.encDec.summaryEncoder.decode(serializedSummary);
451
496
  }
452
497
 
453
498
  /**
454
- * This method writes the log message if the logging is enabled in the extended DDS.
499
+ * This method writes the log message if the logging is enabled in the extended DDS.
455
500
  * The logging is not enabled in the default Property DDS
456
501
  * @param message - The message to be logged.
457
502
  */
458
503
  protected logIfEnabled(message) {}
459
504
 
460
505
  /**
461
- * This method encodes the binary representation of the
462
- * blob.
506
+ * This method encodes the binary representation of the
507
+ * blob.
463
508
  * @param blob - The binary representation of the blob.
464
509
  * @returns The encoded representation of the blob.
465
- */
510
+ */
466
511
  private encodeSummaryBlob(blob: ArrayBuffer): any {
467
512
  return bufferToString(blob, "base64");
468
513
  }
469
514
 
470
515
  /**
471
- * This method decodes the encoded representation of the
516
+ * This method decodes the encoded representation of the
472
517
  * blob.
473
- * @param blob - The encoded representation of the blob.
518
+ * @param blob - The encoded representation of the blob.
474
519
  * @returns The binary representation of the blob.
475
520
  */
476
521
  private decodeSummaryBlob(encoded: any): ArrayBuffer {
@@ -494,13 +539,17 @@ export class SharedPropertyTree extends SharedObject {
494
539
  const summary: ISnapshotSummary = {
495
540
  remoteTipView: this.remoteTipView,
496
541
  remoteChanges: this.remoteChanges,
542
+ remoteHeadGuid: this.headCommitGuid,
497
543
  unrebasedRemoteChanges: this.unrebasedRemoteChanges,
498
544
  };
545
+
499
546
  const chunkSize = 5000 * 1024; // Default limit seems to be 5MB
500
547
  let totalBlobsSize = 0;
501
548
  const serializedSummary = this.encodeSummary(summary);
502
549
  for (let pos = 0, i = 0; pos < serializedSummary.length; pos += chunkSize, i++) {
503
- const summaryBlob = this.encodeSummaryBlob(serializedSummary.slice(pos, pos + chunkSize));
550
+ const summaryBlob = this.encodeSummaryBlob(
551
+ serializedSummary.slice(pos, pos + chunkSize),
552
+ );
504
553
  // eslint-disable-next-line @typescript-eslint/dot-notation
505
554
  totalBlobsSize += summaryBlob["length"];
506
555
  builder.addBlob(`summaryChunk_${i}`, summaryBlob);
@@ -509,9 +558,12 @@ export class SharedPropertyTree extends SharedObject {
509
558
  this.logIfEnabled(`Total blobs transfer size: ${totalBlobsSize}`);
510
559
  }
511
560
 
512
- builder.addBlob("properties", serializer !== undefined
513
- ? serializer.stringify(snapshot, this.handle)
514
- : JSON.stringify(snapshot));
561
+ builder.addBlob(
562
+ "properties",
563
+ serializer !== undefined
564
+ ? serializer.stringify(snapshot, this.handle)
565
+ : JSON.stringify(snapshot),
566
+ );
515
567
  return builder.getSummaryTree();
516
568
  }
517
569
 
@@ -547,11 +599,31 @@ export class SharedPropertyTree extends SharedObject {
547
599
  throw new Error("Invalid Snapshot.");
548
600
  }
549
601
 
602
+ if (snapshotSummary.remoteHeadGuid === undefined) {
603
+ // The summary does not contain a remoteHeadGuid. This means the summary has
604
+ // been created by an old version of PropertyDDS, that did not yet have this patch.
605
+ snapshotSummary.remoteHeadGuid =
606
+ snapshotSummary.remoteChanges.length > 0
607
+ ? // If there are remote changes in the
608
+ // summary we can deduce the head GUID from these changes.
609
+ snapshotSummary.remoteChanges[
610
+ snapshotSummary.remoteChanges.length - 1
611
+ ].guid
612
+ : // If no remote head GUID is available, we will fall back to the old behaviour,
613
+ // where the head GUID was set to an empty string. However, this could lead to
614
+ // divergence between the clients, if there is still a client in the session
615
+ // that is using a version of this library without this patch and which
616
+ // has started the session at a different summary.
617
+ "";
618
+ }
619
+
550
620
  this.remoteTipView = snapshotSummary.remoteTipView;
551
621
  this.remoteChanges = snapshotSummary.remoteChanges;
552
622
  this.unrebasedRemoteChanges = snapshotSummary.unrebasedRemoteChanges;
553
-
554
- const isPartialCheckoutActive = !!(this.options.clientFiltering && this.options.paths);
623
+ this.headCommitGuid = snapshotSummary.remoteHeadGuid;
624
+ const isPartialCheckoutActive = !!(
625
+ this.options.clientFiltering && this.options.paths
626
+ );
555
627
  if (isPartialCheckoutActive && this.options.paths) {
556
628
  this.remoteTipView = ChangeSetUtils.getFilteredChangeSetByPaths(
557
629
  this.remoteTipView,
@@ -563,11 +635,17 @@ export class SharedPropertyTree extends SharedObject {
563
635
  this.skipSequenceNumber = 0;
564
636
  } else {
565
637
  const { branchGuid, summaryMinimumSequenceNumber } = snapshot;
566
- const branchResponse = await axios.get(`http://localhost:3000/branch/${branchGuid}`);
638
+ const branchResponse = await axios.get(
639
+ `http://localhost:3000/branch/${branchGuid}`,
640
+ );
567
641
  this.headCommitGuid = branchResponse.data.headCommitGuid;
568
642
  const {
569
643
  commit: { meta: commitMetadata },
570
- } = (await axios.get(`http://localhost:3000/branch/${branchGuid}/commit/${this.headCommitGuid}`)).data;
644
+ } = (
645
+ await axios.get(
646
+ `http://localhost:3000/branch/${branchGuid}/commit/${this.headCommitGuid}`,
647
+ )
648
+ ).data;
571
649
  let { changeSet: materializedView } = (
572
650
  await axios.get(
573
651
  `http://localhost:3000/branch/${branchGuid}/commit/${this.headCommitGuid}/materializedView`,
@@ -577,7 +655,10 @@ export class SharedPropertyTree extends SharedObject {
577
655
  const isPartialCheckoutActive = this.options.clientFiltering && this.options.paths;
578
656
 
579
657
  if (isPartialCheckoutActive && this.options.paths) {
580
- materializedView = ChangeSetUtils.getFilteredChangeSetByPaths(materializedView, this.options.paths);
658
+ materializedView = ChangeSetUtils.getFilteredChangeSetByPaths(
659
+ materializedView,
660
+ this.options.paths,
661
+ );
581
662
  }
582
663
 
583
664
  this.tipView = materializedView;
@@ -585,19 +666,28 @@ export class SharedPropertyTree extends SharedObject {
585
666
  this.remoteChanges = [];
586
667
 
587
668
  let missingDeltas: ISequencedDocumentMessage[] = [];
588
- const firstDelta = Math.min(commitMetadata.minimumSequenceNumber, summaryMinimumSequenceNumber);
669
+ const firstDelta = Math.min(
670
+ commitMetadata.minimumSequenceNumber,
671
+ summaryMinimumSequenceNumber,
672
+ );
589
673
  const lastDelta = commitMetadata.sequenceNumber;
590
674
 
591
675
  const dm = (this.runtime.deltaManager as any).deltaManager;
592
- await dm.getDeltas("DocumentOpen", firstDelta, lastDelta, (messages: ISequencedDocumentMessage[]) => {
593
- missingDeltas = messages.filter((x) => x.type === "op");
594
- });
676
+ await dm.getDeltas(
677
+ "DocumentOpen",
678
+ firstDelta,
679
+ lastDelta,
680
+ (messages: ISequencedDocumentMessage[]) => {
681
+ missingDeltas = messages.filter((x) => x.type === "op");
682
+ },
683
+ );
595
684
 
596
685
  // eslint-disable-next-line @typescript-eslint/prefer-for-of
597
686
  for (let i = 0; i < missingDeltas.length; i++) {
598
687
  if (missingDeltas[i].sequenceNumber < commitMetadata.sequenceNumber) {
599
- const remoteChange: IPropertyTreeMessage
600
- = JSON.parse(missingDeltas[i].contents).contents.contents.content.contents;
688
+ const remoteChange: IPropertyTreeMessage = JSON.parse(
689
+ missingDeltas[i].contents,
690
+ ).contents.contents.content.contents;
601
691
  const { changeSet } = (
602
692
  await axios.get(
603
693
  `http://localhost:3000/branch/${branchGuid}/commit/${remoteChange.guid}/changeSet`,
@@ -612,7 +702,7 @@ export class SharedPropertyTree extends SharedObject {
612
702
  this.options.paths,
613
703
  );
614
704
  }
615
- this.remoteChanges.push(remoteChange);
705
+ this.addRemoteChange(remoteChange);
616
706
  }
617
707
  } else {
618
708
  this.processCore(missingDeltas[i], false, {});
@@ -635,7 +725,7 @@ export class SharedPropertyTree extends SharedObject {
635
725
  }
636
726
  }
637
727
 
638
- protected onDisconnect() { }
728
+ protected onDisconnect() {}
639
729
 
640
730
  private _applyLocalChangeSet(change: IPropertyTreeMessage) {
641
731
  const changeSetWrapper = new ChangeSet(this.tipView);
@@ -661,14 +751,17 @@ export class SharedPropertyTree extends SharedObject {
661
751
  );
662
752
  }
663
753
 
664
- this.remoteChanges.push(change);
665
-
754
+ this.addRemoteChange(change);
666
755
  // Apply the remote change set to the remote tip view
667
756
  const remoteChangeSetWrapper = new ChangeSet(this.remoteTipView);
668
757
  remoteChangeSetWrapper.applyChangeSet(change.changeSet);
669
758
 
670
759
  // Rebase the local changes
671
- const pendingChanges = this._root._serialize(true, false, BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE);
760
+ const pendingChanges = this._root._serialize(
761
+ true,
762
+ false,
763
+ BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE,
764
+ );
672
765
  new ChangeSet(pendingChanges)._toReversibleChangeSet(this.tipView);
673
766
 
674
767
  const changesToTip: SerializedChangeSet = {};
@@ -731,9 +824,13 @@ export class SharedPropertyTree extends SharedObject {
731
824
  const rebaseMetaInformation = new Map();
732
825
 
733
826
  const copiedChangeSet = new ChangeSet(cloneDeep(this.localChanges[i].changeSet));
734
- new ChangeSet(rebaseBaseChangeSet)._rebaseChangeSet(this.localChanges[i].changeSet, conflicts, {
735
- applyAfterMetaInformation: rebaseMetaInformation,
736
- });
827
+ new ChangeSet(rebaseBaseChangeSet)._rebaseChangeSet(
828
+ this.localChanges[i].changeSet,
829
+ conflicts,
830
+ {
831
+ applyAfterMetaInformation: rebaseMetaInformation,
832
+ },
833
+ );
737
834
 
738
835
  copiedChangeSet.toInverseChangeSet();
739
836
  copiedChangeSet.applyChangeSet(rebaseBaseChangeSet);
@@ -12,21 +12,21 @@ import { DeflatedPropertyTreeFactory, LZ4PropertyTreeFactory } from "./propertyT
12
12
  * the deltas and summaries communicated to the server by Deflate.
13
13
  */
14
14
  export class DeflatedPropertyTree extends SharedPropertyTree {
15
- public static create(runtime: IFluidDataStoreRuntime, id?: string, queryString?: string) {
16
- return runtime.createChannel(id, DeflatedPropertyTreeFactory.Type) as DeflatedPropertyTree;
17
- }
15
+ public static create(runtime: IFluidDataStoreRuntime, id?: string, queryString?: string) {
16
+ return runtime.createChannel(id, DeflatedPropertyTreeFactory.Type) as DeflatedPropertyTree;
17
+ }
18
18
 
19
- public static getFactory(): IChannelFactory {
20
- return new DeflatedPropertyTreeFactory();
21
- }
19
+ public static getFactory(): IChannelFactory {
20
+ return new DeflatedPropertyTreeFactory();
21
+ }
22
22
  }
23
23
 
24
24
  export class LZ4PropertyTree extends SharedPropertyTree {
25
- public static create(runtime: IFluidDataStoreRuntime, id?: string, queryString?: string) {
26
- return runtime.createChannel(id, LZ4PropertyTreeFactory.Type) as LZ4PropertyTree;
27
- }
25
+ public static create(runtime: IFluidDataStoreRuntime, id?: string, queryString?: string) {
26
+ return runtime.createChannel(id, LZ4PropertyTreeFactory.Type) as LZ4PropertyTree;
27
+ }
28
28
 
29
- public static getFactory(): IChannelFactory {
30
- return new LZ4PropertyTreeFactory();
31
- }
29
+ public static getFactory(): IChannelFactory {
30
+ return new LZ4PropertyTreeFactory();
31
+ }
32
32
  }