@fluid-experimental/property-dds 2.0.0-dev.5.2.0.169897 → 2.0.0-dev.6.4.0.191258

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-experimental/property-dds",
3
- "version": "2.0.0-dev.5.2.0.169897",
3
+ "version": "2.0.0-dev.6.4.0.191258",
4
4
  "description": "definition of the property distributed data store",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -15,16 +15,16 @@
15
15
  "module": "lib/index.js",
16
16
  "types": "dist/index.d.ts",
17
17
  "dependencies": {
18
- "@fluid-experimental/property-changeset": "2.0.0-dev.5.2.0.169897",
19
- "@fluid-experimental/property-properties": "2.0.0-dev.5.2.0.169897",
20
- "@fluidframework/common-utils": "^1.1.1",
21
- "@fluidframework/container-definitions": "2.0.0-dev.5.2.0.169897",
22
- "@fluidframework/core-interfaces": "2.0.0-dev.5.2.0.169897",
23
- "@fluidframework/datastore-definitions": "2.0.0-dev.5.2.0.169897",
18
+ "@fluid-experimental/property-changeset": "2.0.0-dev.6.4.0.191258",
19
+ "@fluid-experimental/property-properties": "2.0.0-dev.6.4.0.191258",
20
+ "@fluid-internal/client-utils": "2.0.0-dev.6.4.0.191258",
21
+ "@fluidframework/container-definitions": "2.0.0-dev.6.4.0.191258",
22
+ "@fluidframework/core-interfaces": "2.0.0-dev.6.4.0.191258",
23
+ "@fluidframework/datastore-definitions": "2.0.0-dev.6.4.0.191258",
24
24
  "@fluidframework/protocol-definitions": "^1.1.0",
25
- "@fluidframework/runtime-definitions": "2.0.0-dev.5.2.0.169897",
26
- "@fluidframework/runtime-utils": "2.0.0-dev.5.2.0.169897",
27
- "@fluidframework/shared-object-base": "2.0.0-dev.5.2.0.169897",
25
+ "@fluidframework/runtime-definitions": "2.0.0-dev.6.4.0.191258",
26
+ "@fluidframework/runtime-utils": "2.0.0-dev.6.4.0.191258",
27
+ "@fluidframework/shared-object-base": "2.0.0-dev.6.4.0.191258",
28
28
  "axios": "^0.26.0",
29
29
  "buffer": "^6.0.3",
30
30
  "fastest-json-copy": "^1.0.1",
@@ -32,27 +32,27 @@
32
32
  "lz4js": "^0.2.0",
33
33
  "msgpackr": "^1.4.7",
34
34
  "pako": "^2.0.4",
35
- "uuid": "^8.3.1"
35
+ "uuid": "^9.0.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@fluid-experimental/property-common": "2.0.0-dev.5.2.0.169897",
39
- "@fluid-internal/test-drivers": "2.0.0-dev.5.2.0.169897",
40
- "@fluidframework/build-common": "^1.2.0",
41
- "@fluidframework/container-loader": "2.0.0-dev.5.2.0.169897",
42
- "@fluidframework/driver-definitions": "2.0.0-dev.5.2.0.169897",
43
- "@fluidframework/eslint-config-fluid": "^2.0.0",
44
- "@fluidframework/local-driver": "2.0.0-dev.5.2.0.169897",
45
- "@fluidframework/mocha-test-setup": "2.0.0-dev.5.2.0.169897",
46
- "@fluidframework/sequence": "2.0.0-dev.5.2.0.169897",
47
- "@fluidframework/server-local-server": "^0.1039.1000",
48
- "@fluidframework/test-runtime-utils": "2.0.0-dev.5.2.0.169897",
49
- "@fluidframework/test-utils": "2.0.0-dev.5.2.0.169897",
38
+ "@fluid-experimental/property-common": "2.0.0-dev.6.4.0.191258",
39
+ "@fluid-internal/test-drivers": "2.0.0-dev.6.4.0.191258",
40
+ "@fluidframework/build-common": "^2.0.0",
41
+ "@fluidframework/build-tools": "^0.22.0",
42
+ "@fluidframework/container-loader": "2.0.0-dev.6.4.0.191258",
43
+ "@fluidframework/driver-definitions": "2.0.0-dev.6.4.0.191258",
44
+ "@fluidframework/eslint-config-fluid": "^2.1.0",
45
+ "@fluidframework/local-driver": "2.0.0-dev.6.4.0.191258",
46
+ "@fluidframework/mocha-test-setup": "2.0.0-dev.6.4.0.191258",
47
+ "@fluidframework/sequence": "2.0.0-dev.6.4.0.191258",
48
+ "@fluidframework/server-local-server": "^1.0.1",
49
+ "@fluidframework/test-runtime-utils": "2.0.0-dev.6.4.0.191258",
50
+ "@fluidframework/test-utils": "2.0.0-dev.6.4.0.191258",
50
51
  "@microsoft/api-extractor": "^7.34.4",
51
52
  "@types/lodash": "^4.14.118",
52
53
  "@types/mocha": "^9.1.1",
53
- "@types/node": "^14.18.38",
54
+ "@types/node": "^16.18.38",
54
55
  "chai": "^4.2.0",
55
- "concurrently": "^7.6.0",
56
56
  "copyfiles": "^2.4.1",
57
57
  "cross-env": "^7.0.3",
58
58
  "easy-table": "^1.1.1",
@@ -62,7 +62,6 @@
62
62
  "mocha-json-output-reporter": "^2.0.1",
63
63
  "mocha-multi-reporters": "^1.5.1",
64
64
  "moment": "^2.21.0",
65
- "nyc": "^15.1.0",
66
65
  "prettier": "~2.6.2",
67
66
  "rimraf": "^4.4.0",
68
67
  "typescript": "~4.5.5"
@@ -78,7 +77,7 @@
78
77
  "build:esnext": "tsc --project ./tsconfig.esnext.json",
79
78
  "build:test": "tsc --project ./src/test/tsconfig.json",
80
79
  "ci:build:docs": "api-extractor run --typescript-compiler-folder ../../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../../_api-extractor-temp/",
81
- "clean": "rimraf dist lib *.tsbuildinfo *.build.log",
80
+ "clean": "rimraf --glob 'dist' 'lib' '*.tsbuildinfo' '*.build.log' '_api-extractor-temp' 'nyc'",
82
81
  "eslint": "eslint src",
83
82
  "eslint:fix": "eslint src --fix",
84
83
  "format": "npm run prettier:fix",
@@ -87,9 +86,9 @@
87
86
  "prettier": "prettier --check . --ignore-path ../../../../.prettierignore",
88
87
  "prettier:fix": "prettier --write . --ignore-path ../../../../.prettierignore",
89
88
  "test": "npm run test:mocha",
90
- "test:mocha": "mocha \"dist/**/*.spec.js\" --exit -r node_modules/@fluidframework/mocha-test-setup --unhandled-rejections=strict",
91
- "test:mocha-ts": "cross-env FLUID_TEST_VERBOSE=1 TS_NODE_PROJECT=\"./src/test/tsconfig.json\" mocha --require ts-node/register --extensions ts,tsx \"src/test/**/*.spec.ts\" --exit -r node_modules/@fluidframework/mocha-test-setup --unhandled-rejections=strict --timeout 1500000",
92
- "test:mocha:multireport": "cross-env FLUID_TEST_MULTIREPORT=1 npm run test:mocha",
89
+ "test:mocha": "mocha \"dist/**/*.spec.js\" --exit -r node_modules/@fluidframework/mocha-test-setup",
90
+ "test:mocha-ts": "cross-env FLUID_TEST_VERBOSE=1 TS_NODE_PROJECT=\"./src/test/tsconfig.json\" mocha --require ts-node/register --extensions ts,tsx \"src/test/**/*.spec.ts\" --exit -r node_modules/@fluidframework/mocha-test-setup --timeout 1500000",
91
+ "test:mocha-ts-inspect": "cross-env FLUID_TEST_VERBOSE=1 TS_NODE_PROJECT=\"./src/test/tsconfig.json\" mocha --inspect-brk --require ts-node/register --extensions ts,tsx \"src/test/**/*.spec.ts\" --exit -r node_modules/@fluidframework/mocha-test-setup --timeout 1500000",
93
92
  "test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
94
93
  "tsc": "tsc"
95
94
  }
@@ -6,6 +6,8 @@
6
6
  /* eslint-disable import/no-internal-modules */
7
7
  import isEmpty from "lodash/isEmpty";
8
8
  import findIndex from "lodash/findIndex";
9
+ import find from "lodash/find";
10
+ import isEqual from "lodash/isEqual";
9
11
  import range from "lodash/range";
10
12
  import { copy as cloneDeep } from "fastest-json-copy";
11
13
  import { Packr } from "msgpackr";
@@ -19,7 +21,7 @@ import {
19
21
  IChannelFactory,
20
22
  } from "@fluidframework/datastore-definitions";
21
23
 
22
- import { bufferToString, stringToBuffer } from "@fluidframework/common-utils";
24
+ import { bufferToString, stringToBuffer } from "@fluid-internal/client-utils";
23
25
  import { ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
24
26
  import { IFluidSerializer, SharedObject } from "@fluidframework/shared-object-base";
25
27
  import { SummaryTreeBuilder } from "@fluidframework/runtime-utils";
@@ -84,6 +86,7 @@ export interface SharedPropertyTreeOptions {
84
86
  paths?: string[];
85
87
  clientFiltering?: boolean;
86
88
  useMH?: boolean;
89
+ disablePartialCheckout?: boolean;
87
90
  }
88
91
 
89
92
  export interface ISharedPropertyTreeEncDec {
@@ -195,12 +198,14 @@ export class SharedPropertyTree extends SharedObject {
195
198
  private scopeFutureDeltasToPaths(paths?: string[]) {
196
199
  // Backdoor to emit "partial_checkout" events on the socket. The delta manager at container runtime layer is
197
200
  // a proxy and the delta manager at the container context layer is yet another proxy, so account for that.
198
- let dm = (this.runtime.deltaManager as any).deltaManager;
199
- if (dm.deltaManager !== undefined) {
200
- dm = dm.deltaManager;
201
+ if (!this.options.disablePartialCheckout) {
202
+ let dm = (this.runtime.deltaManager as any).deltaManager;
203
+ if (dm.deltaManager !== undefined) {
204
+ dm = dm.deltaManager;
205
+ }
206
+ const socket = dm.connectionManager.connection.socket;
207
+ socket.emit("partial_checkout", { paths });
201
208
  }
202
- const socket = dm.connectionManager.connection.socket;
203
- socket.emit("partial_checkout", { paths });
204
209
  }
205
210
 
206
211
  public _reportDirtinessToView() {
@@ -678,6 +683,7 @@ export class SharedPropertyTree extends SharedObject {
678
683
  const lastDelta = commitMetadata.sequenceNumber;
679
684
 
680
685
  const dm = (this.runtime.deltaManager as any).deltaManager;
686
+ // TODO: This is accessing a private member of the delta manager, and should not be.
681
687
  await dm.getDeltas(
682
688
  "DocumentOpen",
683
689
  firstDelta,
@@ -690,8 +696,11 @@ export class SharedPropertyTree extends SharedObject {
690
696
  // eslint-disable-next-line @typescript-eslint/prefer-for-of
691
697
  for (let i = 0; i < missingDeltas.length; i++) {
692
698
  if (missingDeltas[i].sequenceNumber < commitMetadata.sequenceNumber) {
699
+ // TODO: Don't spy on the DeltaManager's private internals.
700
+ // This is trying to mimic what DeltaManager does in processInboundMessage, but there's no guarantee that
701
+ // private implementation won't change.
693
702
  const remoteChange: IPropertyTreeMessage = JSON.parse(
694
- missingDeltas[i].contents,
703
+ missingDeltas[i].contents as string,
695
704
  ).contents.contents.content.contents;
696
705
  const { changeSet } = (
697
706
  await axios.get(
@@ -791,6 +800,10 @@ export class SharedPropertyTree extends SharedObject {
791
800
 
792
801
  getRebasedChanges(startGuid: string, endGuid?: string) {
793
802
  const startIndex = findIndex(this.remoteChanges, (c) => c.guid === startGuid);
803
+ if (startIndex === -1 && startGuid !== "") {
804
+ // TODO: Consider throwing an error once clients have picked up PR #16277.
805
+ console.error("Unknown start GUID specified.");
806
+ }
794
807
  if (endGuid !== undefined) {
795
808
  const endIndex = findIndex(this.remoteChanges, (c) => c.guid === endGuid);
796
809
  return this.remoteChanges.slice(startIndex + 1, endIndex + 1);
@@ -803,7 +816,7 @@ export class SharedPropertyTree extends SharedObject {
803
816
  pendingChanges: SerializedChangeSet,
804
817
  newTipDelta: SerializedChangeSet,
805
818
  ): boolean {
806
- let rebaseBaseChangeSet = cloneDeep(change.changeSet);
819
+ let rebaseBaseChangeSet;
807
820
 
808
821
  const accumulatedChanges: SerializedChangeSet = {};
809
822
  const conflicts = [] as any[];
@@ -813,15 +826,29 @@ export class SharedPropertyTree extends SharedObject {
813
826
  // assert(JSON.stringify(this.localChanges[0].changeSet) === JSON.stringify(change.changeSet),
814
827
  // "Local change different than rebased remote change.");
815
828
 
816
- // If we got a confirmation of the commit on the tip of the localChanges array,
817
- // there will be no update of the tip view at all. We just move it from local changes
818
- // to remote changes
819
- this.localChanges.shift();
829
+ if (isEqual(this.localChanges[0].changeSet, change.changeSet)) {
830
+ // If we got a confirmation of the commit on the tip of the localChanges array,
831
+ // there will be no update of the tip view at all. We just move it from local changes
832
+ // to remote changes
833
+ this.localChanges.shift();
820
834
 
821
- return false;
835
+ return false;
836
+ } else {
837
+ // There is a case where the localChanges that were created by incrementally rebasing with respect
838
+ // to every incoming change do no exactly agree with the rebased remote change (this happens
839
+ // when there are changes that cancel out with each other that have happened in the meantime).
840
+ // In that case, we must make sure, we correctly update the local view to take this difference into
841
+ // account by rebasing with respect to the changeset that is obtained by combining the inverse of the
842
+ // local change with the incoming remote change.
843
+
844
+ rebaseBaseChangeSet = new ChangeSet(this.localChanges.shift()?.changeSet);
845
+ rebaseBaseChangeSet.toInverseChangeSet();
846
+ rebaseBaseChangeSet.applyChangeSet(change.changeSet);
847
+ }
848
+ } else {
849
+ rebaseBaseChangeSet = cloneDeep(change.changeSet);
822
850
  }
823
851
 
824
- // eslint-disable-next-line @typescript-eslint/prefer-for-of
825
852
  for (let i = 0; i < this.localChanges.length; i++) {
826
853
  // Make sure we never receive changes out of order
827
854
  console.assert(this.localChanges[i].guid !== change.guid);
@@ -845,6 +872,12 @@ export class SharedPropertyTree extends SharedObject {
845
872
  rebaseBaseChangeSet = copiedChangeSet.getSerializedChangeSet();
846
873
 
847
874
  new ChangeSet(accumulatedChanges).applyChangeSet(this.localChanges[i].changeSet);
875
+
876
+ // Update the reference and head guids
877
+ this.localChanges[i].remoteHeadGuid = change.guid;
878
+ if (i === 0) {
879
+ this.localChanges[i].referenceGuid = change.guid;
880
+ }
848
881
  }
849
882
 
850
883
  // Compute the inverse of the pending changes and store the result in newTipDelta
@@ -874,6 +907,26 @@ export class SharedPropertyTree extends SharedObject {
874
907
  return true;
875
908
  }
876
909
 
910
+ protected reSubmitCore(content: any, localOpMetadata: unknown) {
911
+ // We have to provide our own implementation of the resubmit core function, to
912
+ // handle the case where an operation is no longer referencing a commit within
913
+ // the collaboration window as its referenceGuid. Other clients would not be
914
+ // able to perform the rebase for such an operation. To handle this problem
915
+ // we have to resubmit a version of the operations which has been rebased to
916
+ // the current remote tip. We already have these rebased versions of the operations
917
+ // in our localChanges, because we continuously update those to follow the tip.
918
+ // Therefore our reSubmitCore function searches for the rebased operation in the
919
+ // localChanges array and submits this up-to-date version instead of the old operation.
920
+ const rebasedOperation = find(this.localChanges, (op) => op.guid === content.guid);
921
+
922
+ if (rebasedOperation) {
923
+ this.submitLocalMessage(cloneDeep(rebasedOperation), localOpMetadata);
924
+ } else {
925
+ // Could this happen or is there a guard that we will never resubmit an already submitted op?
926
+ console.warn("Resubmitting operation which has already been received back.");
927
+ }
928
+ }
929
+
877
930
  protected applyStashedOp() {
878
931
  throw new Error("not implemented");
879
932
  }
@@ -5,7 +5,7 @@
5
5
  /* eslint-disable @typescript-eslint/no-unsafe-return */
6
6
  import { deflate, inflate } from "pako";
7
7
  import { compress, decompress } from "lz4js";
8
- import { bufferToString, stringToBuffer } from "@fluidframework/common-utils";
8
+ import { bufferToString, stringToBuffer } from "@fluid-internal/client-utils";
9
9
  import {
10
10
  IChannelAttributes,
11
11
  IFluidDataStoreRuntime,