@fluidframework/merge-tree 2.30.0 → 2.31.0
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/CHANGELOG.md +403 -399
- package/api-report/merge-tree.legacy.alpha.api.md +1 -0
- package/dist/MergeTreeTextHelper.d.ts +9 -3
- package/dist/MergeTreeTextHelper.d.ts.map +1 -1
- package/dist/MergeTreeTextHelper.js +5 -5
- package/dist/MergeTreeTextHelper.js.map +1 -1
- package/dist/client.d.ts +7 -13
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +136 -110
- package/dist/client.js.map +1 -1
- package/dist/endOfTreeSegment.d.ts +12 -8
- package/dist/endOfTreeSegment.d.ts.map +1 -1
- package/dist/endOfTreeSegment.js +2 -4
- package/dist/endOfTreeSegment.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/mergeTree.d.ts +37 -23
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +400 -483
- package/dist/mergeTree.js.map +1 -1
- package/dist/mergeTreeDeltaCallback.d.ts +4 -8
- package/dist/mergeTreeDeltaCallback.d.ts.map +1 -1
- package/dist/mergeTreeDeltaCallback.js.map +1 -1
- package/dist/mergeTreeNodes.d.ts +32 -10
- package/dist/mergeTreeNodes.d.ts.map +1 -1
- package/dist/mergeTreeNodes.js +43 -28
- package/dist/mergeTreeNodes.js.map +1 -1
- package/dist/partialLengths.d.ts +2 -2
- package/dist/partialLengths.d.ts.map +1 -1
- package/dist/partialLengths.js +181 -109
- package/dist/partialLengths.js.map +1 -1
- package/dist/perspective.d.ts +8 -27
- package/dist/perspective.d.ts.map +1 -1
- package/dist/perspective.js +7 -67
- package/dist/perspective.js.map +1 -1
- package/dist/revertibles.d.ts.map +1 -1
- package/dist/revertibles.js +2 -2
- package/dist/revertibles.js.map +1 -1
- package/dist/segmentInfos.d.ts +20 -106
- package/dist/segmentInfos.d.ts.map +1 -1
- package/dist/segmentInfos.js +28 -42
- package/dist/segmentInfos.js.map +1 -1
- package/dist/segmentPropertiesManager.d.ts +1 -14
- package/dist/segmentPropertiesManager.d.ts.map +1 -1
- package/dist/segmentPropertiesManager.js +3 -17
- package/dist/segmentPropertiesManager.js.map +1 -1
- package/dist/snapshotLoader.d.ts.map +1 -1
- package/dist/snapshotLoader.js +62 -19
- package/dist/snapshotLoader.js.map +1 -1
- package/dist/snapshotV1.d.ts.map +1 -1
- package/dist/snapshotV1.js +55 -24
- package/dist/snapshotV1.js.map +1 -1
- package/dist/snapshotlegacy.d.ts.map +1 -1
- package/dist/snapshotlegacy.js +6 -9
- package/dist/snapshotlegacy.js.map +1 -1
- package/dist/stamps.d.ts +1 -1
- package/dist/stamps.js +1 -1
- package/dist/stamps.js.map +1 -1
- package/dist/test/Insertion.perf.spec.js +6 -51
- package/dist/test/Insertion.perf.spec.js.map +1 -1
- package/dist/test/PartialLengths.perf.spec.js +18 -25
- package/dist/test/PartialLengths.perf.spec.js.map +1 -1
- package/dist/test/Removal.perf.spec.js +13 -41
- package/dist/test/Removal.perf.spec.js.map +1 -1
- package/dist/test/beastTest.spec.d.ts.map +1 -1
- package/dist/test/beastTest.spec.js +41 -66
- package/dist/test/beastTest.spec.js.map +1 -1
- package/dist/test/client.annotateMarker.spec.js +1 -11
- package/dist/test/client.annotateMarker.spec.js.map +1 -1
- package/dist/test/client.applyMsg.spec.js +14 -14
- package/dist/test/client.applyMsg.spec.js.map +1 -1
- package/dist/test/client.getPosition.spec.js +1 -1
- package/dist/test/client.getPosition.spec.js.map +1 -1
- package/dist/test/client.localReference.spec.js +1 -1
- package/dist/test/client.localReference.spec.js.map +1 -1
- package/dist/test/client.rollback.spec.js +49 -58
- package/dist/test/client.rollback.spec.js.map +1 -1
- package/dist/test/client.rollbackFarm.spec.js +1 -1
- package/dist/test/client.rollbackFarm.spec.js.map +1 -1
- package/dist/test/client.searchForMarker.spec.js +4 -21
- package/dist/test/client.searchForMarker.spec.js.map +1 -1
- package/dist/test/index.d.ts +2 -2
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +2 -6
- package/dist/test/index.js.map +1 -1
- package/dist/test/mergeTree.annotate.deltaCallback.spec.js +14 -59
- package/dist/test/mergeTree.annotate.deltaCallback.spec.js.map +1 -1
- package/dist/test/mergeTree.annotate.spec.js +47 -63
- package/dist/test/mergeTree.annotate.spec.js.map +1 -1
- package/dist/test/mergeTree.insert.deltaCallback.spec.js +9 -62
- package/dist/test/mergeTree.insert.deltaCallback.spec.js.map +1 -1
- package/dist/test/mergeTree.insertingWalk.spec.js +59 -125
- package/dist/test/mergeTree.insertingWalk.spec.js.map +1 -1
- package/dist/test/mergeTree.markRangeRemoved.deltaCallback.spec.js +12 -93
- package/dist/test/mergeTree.markRangeRemoved.deltaCallback.spec.js.map +1 -1
- package/dist/test/mergeTree.markRangeRemoved.spec.js +10 -7
- package/dist/test/mergeTree.markRangeRemoved.spec.js.map +1 -1
- package/dist/test/mergeTree.walk.spec.js +2 -14
- package/dist/test/mergeTree.walk.spec.js.map +1 -1
- package/dist/test/mergeTreeOperationRunner.js +2 -2
- package/dist/test/mergeTreeOperationRunner.js.map +1 -1
- package/dist/test/obliterate.concurrent.spec.js +18 -23
- package/dist/test/obliterate.concurrent.spec.js.map +1 -1
- package/dist/test/obliterate.partialLength.spec.js +166 -136
- package/dist/test/obliterate.partialLength.spec.js.map +1 -1
- package/dist/test/obliterate.spec.js +16 -126
- package/dist/test/obliterate.spec.js.map +1 -1
- package/dist/test/partialLength.spec.js +28 -196
- package/dist/test/partialLength.spec.js.map +1 -1
- package/dist/test/perspective.spec.js +34 -0
- package/dist/test/perspective.spec.js.map +1 -1
- package/dist/test/propertyManager.spec.js +1 -1
- package/dist/test/propertyManager.spec.js.map +1 -1
- package/dist/test/resetPendingSegmentsToOp.spec.js +0 -2
- package/dist/test/resetPendingSegmentsToOp.spec.js.map +1 -1
- package/dist/test/segmentGroupCollection.spec.js +10 -4
- package/dist/test/segmentGroupCollection.spec.js.map +1 -1
- package/dist/test/testClient.d.ts +1 -0
- package/dist/test/testClient.d.ts.map +1 -1
- package/dist/test/testClient.js +16 -26
- package/dist/test/testClient.js.map +1 -1
- package/dist/test/testClientLogger.d.ts.map +1 -1
- package/dist/test/testClientLogger.js +3 -10
- package/dist/test/testClientLogger.js.map +1 -1
- package/dist/test/testServer.d.ts +2 -1
- package/dist/test/testServer.d.ts.map +1 -1
- package/dist/test/testServer.js +7 -5
- package/dist/test/testServer.js.map +1 -1
- package/dist/test/testUtils.d.ts +36 -56
- package/dist/test/testUtils.d.ts.map +1 -1
- package/dist/test/testUtils.js +68 -77
- package/dist/test/testUtils.js.map +1 -1
- package/dist/test/text.d.ts +2 -2
- package/dist/test/text.d.ts.map +1 -1
- package/dist/test/text.js +5 -2
- package/dist/test/text.js.map +1 -1
- package/dist/textSegment.d.ts +0 -6
- package/dist/textSegment.d.ts.map +1 -1
- package/dist/textSegment.js.map +1 -1
- package/dist/zamboni.d.ts.map +1 -1
- package/dist/zamboni.js +53 -26
- package/dist/zamboni.js.map +1 -1
- package/lib/MergeTreeTextHelper.d.ts +9 -3
- package/lib/MergeTreeTextHelper.d.ts.map +1 -1
- package/lib/MergeTreeTextHelper.js +5 -5
- package/lib/MergeTreeTextHelper.js.map +1 -1
- package/lib/client.d.ts +7 -13
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +117 -116
- package/lib/client.js.map +1 -1
- package/lib/endOfTreeSegment.d.ts +12 -8
- package/lib/endOfTreeSegment.d.ts.map +1 -1
- package/lib/endOfTreeSegment.js +2 -4
- package/lib/endOfTreeSegment.js.map +1 -1
- package/lib/index.d.ts +6 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/mergeTree.d.ts +37 -23
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +381 -488
- package/lib/mergeTree.js.map +1 -1
- package/lib/mergeTreeDeltaCallback.d.ts +4 -8
- package/lib/mergeTreeDeltaCallback.d.ts.map +1 -1
- package/lib/mergeTreeDeltaCallback.js.map +1 -1
- package/lib/mergeTreeNodes.d.ts +32 -10
- package/lib/mergeTreeNodes.d.ts.map +1 -1
- package/lib/mergeTreeNodes.js +42 -29
- package/lib/mergeTreeNodes.js.map +1 -1
- package/lib/partialLengths.d.ts +2 -2
- package/lib/partialLengths.d.ts.map +1 -1
- package/lib/partialLengths.js +160 -111
- package/lib/partialLengths.js.map +1 -1
- package/lib/perspective.d.ts +8 -27
- package/lib/perspective.d.ts.map +1 -1
- package/lib/perspective.js +8 -68
- package/lib/perspective.js.map +1 -1
- package/lib/revertibles.d.ts.map +1 -1
- package/lib/revertibles.js +2 -2
- package/lib/revertibles.js.map +1 -1
- package/lib/segmentInfos.d.ts +20 -106
- package/lib/segmentInfos.d.ts.map +1 -1
- package/lib/segmentInfos.js +26 -37
- package/lib/segmentInfos.js.map +1 -1
- package/lib/segmentPropertiesManager.d.ts +1 -14
- package/lib/segmentPropertiesManager.d.ts.map +1 -1
- package/lib/segmentPropertiesManager.js +2 -16
- package/lib/segmentPropertiesManager.js.map +1 -1
- package/lib/snapshotLoader.d.ts.map +1 -1
- package/lib/snapshotLoader.js +39 -19
- package/lib/snapshotLoader.js.map +1 -1
- package/lib/snapshotV1.d.ts.map +1 -1
- package/lib/snapshotV1.js +34 -26
- package/lib/snapshotV1.js.map +1 -1
- package/lib/snapshotlegacy.d.ts.map +1 -1
- package/lib/snapshotlegacy.js +7 -10
- package/lib/snapshotlegacy.js.map +1 -1
- package/lib/stamps.d.ts +1 -1
- package/lib/stamps.js +1 -1
- package/lib/stamps.js.map +1 -1
- package/lib/test/Insertion.perf.spec.js +6 -51
- package/lib/test/Insertion.perf.spec.js.map +1 -1
- package/lib/test/PartialLengths.perf.spec.js +18 -25
- package/lib/test/PartialLengths.perf.spec.js.map +1 -1
- package/lib/test/Removal.perf.spec.js +13 -41
- package/lib/test/Removal.perf.spec.js.map +1 -1
- package/lib/test/beastTest.spec.d.ts.map +1 -1
- package/lib/test/beastTest.spec.js +42 -67
- package/lib/test/beastTest.spec.js.map +1 -1
- package/lib/test/client.annotateMarker.spec.js +1 -11
- package/lib/test/client.annotateMarker.spec.js.map +1 -1
- package/lib/test/client.applyMsg.spec.js +14 -14
- package/lib/test/client.applyMsg.spec.js.map +1 -1
- package/lib/test/client.getPosition.spec.js +1 -1
- package/lib/test/client.getPosition.spec.js.map +1 -1
- package/lib/test/client.localReference.spec.js +1 -1
- package/lib/test/client.localReference.spec.js.map +1 -1
- package/lib/test/client.rollback.spec.js +50 -59
- package/lib/test/client.rollback.spec.js.map +1 -1
- package/lib/test/client.rollbackFarm.spec.js +1 -1
- package/lib/test/client.rollbackFarm.spec.js.map +1 -1
- package/lib/test/client.searchForMarker.spec.js +4 -21
- package/lib/test/client.searchForMarker.spec.js.map +1 -1
- package/lib/test/index.d.ts +2 -2
- package/lib/test/index.d.ts.map +1 -1
- package/lib/test/index.js +1 -1
- package/lib/test/index.js.map +1 -1
- package/lib/test/mergeTree.annotate.deltaCallback.spec.js +15 -60
- package/lib/test/mergeTree.annotate.deltaCallback.spec.js.map +1 -1
- package/lib/test/mergeTree.annotate.spec.js +48 -64
- package/lib/test/mergeTree.annotate.spec.js.map +1 -1
- package/lib/test/mergeTree.insert.deltaCallback.spec.js +10 -63
- package/lib/test/mergeTree.insert.deltaCallback.spec.js.map +1 -1
- package/lib/test/mergeTree.insertingWalk.spec.js +61 -127
- package/lib/test/mergeTree.insertingWalk.spec.js.map +1 -1
- package/lib/test/mergeTree.markRangeRemoved.deltaCallback.spec.js +13 -94
- package/lib/test/mergeTree.markRangeRemoved.deltaCallback.spec.js.map +1 -1
- package/lib/test/mergeTree.markRangeRemoved.spec.js +10 -7
- package/lib/test/mergeTree.markRangeRemoved.spec.js.map +1 -1
- package/lib/test/mergeTree.walk.spec.js +2 -14
- package/lib/test/mergeTree.walk.spec.js.map +1 -1
- package/lib/test/mergeTreeOperationRunner.js +3 -3
- package/lib/test/mergeTreeOperationRunner.js.map +1 -1
- package/lib/test/obliterate.concurrent.spec.js +18 -23
- package/lib/test/obliterate.concurrent.spec.js.map +1 -1
- package/lib/test/obliterate.partialLength.spec.js +167 -137
- package/lib/test/obliterate.partialLength.spec.js.map +1 -1
- package/lib/test/obliterate.spec.js +17 -127
- package/lib/test/obliterate.spec.js.map +1 -1
- package/lib/test/partialLength.spec.js +29 -197
- package/lib/test/partialLength.spec.js.map +1 -1
- package/lib/test/perspective.spec.js +34 -0
- package/lib/test/perspective.spec.js.map +1 -1
- package/lib/test/propertyManager.spec.js +2 -2
- package/lib/test/propertyManager.spec.js.map +1 -1
- package/lib/test/resetPendingSegmentsToOp.spec.js +0 -2
- package/lib/test/resetPendingSegmentsToOp.spec.js.map +1 -1
- package/lib/test/segmentGroupCollection.spec.js +10 -4
- package/lib/test/segmentGroupCollection.spec.js.map +1 -1
- package/lib/test/testClient.d.ts +1 -0
- package/lib/test/testClient.d.ts.map +1 -1
- package/lib/test/testClient.js +18 -28
- package/lib/test/testClient.js.map +1 -1
- package/lib/test/testClientLogger.d.ts.map +1 -1
- package/lib/test/testClientLogger.js +3 -10
- package/lib/test/testClientLogger.js.map +1 -1
- package/lib/test/testServer.d.ts +2 -1
- package/lib/test/testServer.d.ts.map +1 -1
- package/lib/test/testServer.js +7 -5
- package/lib/test/testServer.js.map +1 -1
- package/lib/test/testUtils.d.ts +36 -56
- package/lib/test/testUtils.d.ts.map +1 -1
- package/lib/test/testUtils.js +66 -48
- package/lib/test/testUtils.js.map +1 -1
- package/lib/test/text.d.ts +2 -2
- package/lib/test/text.d.ts.map +1 -1
- package/lib/test/text.js +6 -3
- package/lib/test/text.js.map +1 -1
- package/lib/textSegment.d.ts +0 -6
- package/lib/textSegment.d.ts.map +1 -1
- package/lib/textSegment.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/lib/zamboni.d.ts.map +1 -1
- package/lib/zamboni.js +32 -28
- package/lib/zamboni.js.map +1 -1
- package/package.json +17 -20
- package/src/MergeTreeTextHelper.ts +17 -12
- package/src/client.ts +141 -197
- package/src/endOfTreeSegment.ts +11 -8
- package/src/index.ts +4 -3
- package/src/mergeTree.ts +482 -633
- package/src/mergeTreeDeltaCallback.ts +4 -8
- package/src/mergeTreeNodes.ts +66 -45
- package/src/partialLengths.ts +181 -137
- package/src/perspective.ts +17 -95
- package/src/revertibles.ts +2 -7
- package/src/segmentInfos.ts +48 -141
- package/src/segmentPropertiesManager.ts +2 -16
- package/src/snapshotLoader.ts +62 -30
- package/src/snapshotV1.ts +36 -28
- package/src/snapshotlegacy.ts +7 -16
- package/src/stamps.ts +1 -1
- package/src/textSegment.ts +0 -13
- package/src/zamboni.ts +38 -32
- package/tsconfig.json +1 -0
- package/prettier.config.cjs +0 -8
package/src/revertibles.ts
CHANGED
|
@@ -308,8 +308,7 @@ function revertLocalRemove(
|
|
|
308
308
|
driver.insertFromSpec(realPos, props.segSpec);
|
|
309
309
|
const insertSegment = mergeTreeWithRevert.getContainingSegment(
|
|
310
310
|
realPos,
|
|
311
|
-
mergeTreeWithRevert.
|
|
312
|
-
mergeTreeWithRevert.collabWindow.clientId,
|
|
311
|
+
mergeTreeWithRevert.localPerspective,
|
|
313
312
|
).segment;
|
|
314
313
|
assertSegmentLeaf(insertSegment);
|
|
315
314
|
|
|
@@ -395,11 +394,7 @@ function revertLocalAnnotate(
|
|
|
395
394
|
}
|
|
396
395
|
|
|
397
396
|
function getPosition(mergeTreeWithRevert: MergeTreeWithRevert, segment: ISegmentLeaf): number {
|
|
398
|
-
return mergeTreeWithRevert.getPosition(
|
|
399
|
-
segment,
|
|
400
|
-
mergeTreeWithRevert.collabWindow.currentSeq,
|
|
401
|
-
mergeTreeWithRevert.collabWindow.clientId,
|
|
402
|
-
);
|
|
397
|
+
return mergeTreeWithRevert.getPosition(segment, mergeTreeWithRevert.localPerspective);
|
|
403
398
|
}
|
|
404
399
|
|
|
405
400
|
/**
|
package/src/segmentInfos.ts
CHANGED
|
@@ -7,13 +7,13 @@ import { assert, isObject } from "@fluidframework/core-utils/internal";
|
|
|
7
7
|
|
|
8
8
|
import { UnassignedSequenceNumber } from "./constants.js";
|
|
9
9
|
import { ISegmentInternal, ISegmentPrivate, MergeBlock } from "./mergeTreeNodes.js";
|
|
10
|
-
import type {
|
|
10
|
+
import type { InsertOperationStamp, RemoveOperationStamp } from "./stamps.js";
|
|
11
11
|
|
|
12
12
|
export interface StringToType {
|
|
13
13
|
"string": string;
|
|
14
14
|
"number": number;
|
|
15
15
|
"object": object;
|
|
16
|
-
"array": [];
|
|
16
|
+
"array": unknown[];
|
|
17
17
|
"boolean": boolean;
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -46,25 +46,8 @@ export function propInstanceOf<P extends string, T>(
|
|
|
46
46
|
/**
|
|
47
47
|
* Contains insertion information associated to an {@link ISegment}.
|
|
48
48
|
*/
|
|
49
|
-
export interface
|
|
50
|
-
|
|
51
|
-
* Short clientId for the client that inserted this segment.
|
|
52
|
-
*/
|
|
53
|
-
clientId: number;
|
|
54
|
-
/**
|
|
55
|
-
* Local seq at which this segment was inserted.
|
|
56
|
-
* This is defined if and only if the insertion of the segment is pending ack, i.e. `seq` is UnassignedSequenceNumber.
|
|
57
|
-
* Once the segment is acked, this field is cleared.
|
|
58
|
-
*
|
|
59
|
-
* @privateRemarks
|
|
60
|
-
* See {@link CollaborationWindow.localSeq} for more information on the semantics of localSeq.
|
|
61
|
-
*/
|
|
62
|
-
localSeq?: number;
|
|
63
|
-
/**
|
|
64
|
-
* Seq at which this segment was inserted.
|
|
65
|
-
* If undefined, it is assumed the segment was inserted prior to the collab window's minimum sequence number.
|
|
66
|
-
*/
|
|
67
|
-
seq: number;
|
|
49
|
+
export interface IHasInsertionInfo {
|
|
50
|
+
insert: InsertOperationStamp;
|
|
68
51
|
}
|
|
69
52
|
|
|
70
53
|
/**
|
|
@@ -73,10 +56,14 @@ export interface IInsertionInfo {
|
|
|
73
56
|
* @param segmentLike - The segment-like object to convert.
|
|
74
57
|
* @returns The insertion info object if the conversion is possible, otherwise undefined.
|
|
75
58
|
*/
|
|
76
|
-
export const toInsertionInfo = (segmentLike: unknown):
|
|
77
|
-
|
|
78
|
-
|
|
59
|
+
export const toInsertionInfo = (segmentLike: unknown): IHasInsertionInfo | undefined => {
|
|
60
|
+
return segmentLike !== undefined &&
|
|
61
|
+
hasProp(segmentLike, "insert", "object") &&
|
|
62
|
+
hasProp(segmentLike.insert, "clientId", "number") &&
|
|
63
|
+
hasProp(segmentLike.insert, "seq", "number")
|
|
64
|
+
? (segmentLike as IHasInsertionInfo)
|
|
79
65
|
: undefined;
|
|
66
|
+
};
|
|
80
67
|
|
|
81
68
|
/**
|
|
82
69
|
* A type-guard which determines if the segment has insertion info, and
|
|
@@ -85,7 +72,7 @@ export const toInsertionInfo = (segmentLike: unknown): IInsertionInfo | undefine
|
|
|
85
72
|
* @param segmentLike - The segment-like object to check.
|
|
86
73
|
* @returns True if the segment has insertion info, otherwise false.
|
|
87
74
|
*/
|
|
88
|
-
export const isInserted = (segmentLike: unknown): segmentLike is
|
|
75
|
+
export const isInserted = (segmentLike: unknown): segmentLike is IHasInsertionInfo =>
|
|
89
76
|
toInsertionInfo(segmentLike) !== undefined;
|
|
90
77
|
|
|
91
78
|
/**
|
|
@@ -94,9 +81,9 @@ export const isInserted = (segmentLike: unknown): segmentLike is IInsertionInfo
|
|
|
94
81
|
* @param segmentLike - The segment-like object to check.
|
|
95
82
|
* @throws Will throw an error if the segment does not have insertion info.
|
|
96
83
|
*/
|
|
97
|
-
export const assertInserted: <T extends Partial<
|
|
98
|
-
segmentLike: ISegmentInternal | Partial<
|
|
99
|
-
) => asserts segmentLike is
|
|
84
|
+
export const assertInserted: <T extends Partial<IHasInsertionInfo> | undefined>(
|
|
85
|
+
segmentLike: ISegmentInternal | Partial<IHasInsertionInfo> | T,
|
|
86
|
+
) => asserts segmentLike is IHasInsertionInfo | Exclude<T, Partial<IHasInsertionInfo>> = (
|
|
100
87
|
segmentLike,
|
|
101
88
|
) =>
|
|
102
89
|
assert(
|
|
@@ -181,24 +168,15 @@ export const removeMergeNodeInfo: (nodeLike: IMergeNodeInfo) => asserts nodeLike
|
|
|
181
168
|
});
|
|
182
169
|
|
|
183
170
|
/**
|
|
184
|
-
* Contains removal information associated
|
|
171
|
+
* Contains removal information associated with an {@link ISegment}.
|
|
172
|
+
*
|
|
173
|
+
* Segments can be removed concurrently by multiple clients.
|
|
185
174
|
*/
|
|
186
|
-
export interface
|
|
187
|
-
/**
|
|
188
|
-
* Local seq at which this segment was removed, if the removal is yet-to-be acked.
|
|
189
|
-
*/
|
|
190
|
-
localRemovedSeq?: number;
|
|
175
|
+
export interface IHasRemovalInfo {
|
|
191
176
|
/**
|
|
192
|
-
*
|
|
177
|
+
* Operation stamps which have removed this segment. This list is sorted by stamp order, where removes[0] is the earliest removal.
|
|
193
178
|
*/
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* List of client IDs that have removed this segment.
|
|
197
|
-
* The client that actually removed the segment (i.e. whose removal op was sequenced first) is stored as the first
|
|
198
|
-
* client in this list. Other clients in the list have all issued concurrent ops to remove the segment.
|
|
199
|
-
* @remarks When this list has length \> 1, this is referred to as the "overlapping remove" case.
|
|
200
|
-
*/
|
|
201
|
-
removedClientIds: number[];
|
|
179
|
+
removes: RemoveOperationStamp[];
|
|
202
180
|
}
|
|
203
181
|
|
|
204
182
|
/**
|
|
@@ -207,11 +185,14 @@ export interface IRemovalInfo {
|
|
|
207
185
|
* @param segmentLike - The segment-like object to convert.
|
|
208
186
|
* @returns The removal info object if the conversion is possible, otherwise undefined.
|
|
209
187
|
*/
|
|
210
|
-
export const toRemovalInfo = (segmentLike: unknown):
|
|
211
|
-
hasProp(segmentLike, "
|
|
212
|
-
|
|
213
|
-
|
|
188
|
+
export const toRemovalInfo = (segmentLike: unknown): IHasRemovalInfo | undefined => {
|
|
189
|
+
return hasProp(segmentLike, "removes", "array") &&
|
|
190
|
+
segmentLike.removes.length > 0 &&
|
|
191
|
+
hasProp(segmentLike.removes[0], "clientId", "number") &&
|
|
192
|
+
hasProp(segmentLike.removes[0], "seq", "number")
|
|
193
|
+
? (segmentLike as IHasRemovalInfo)
|
|
214
194
|
: undefined;
|
|
195
|
+
};
|
|
215
196
|
|
|
216
197
|
/**
|
|
217
198
|
* A type-guard which determines if the segment has removal info, and
|
|
@@ -220,7 +201,10 @@ export const toRemovalInfo = (segmentLike: unknown): IRemovalInfo | undefined =>
|
|
|
220
201
|
* @param segmentLike - The segment-like object to check.
|
|
221
202
|
* @returns True if the segment has removal info, otherwise false.
|
|
222
203
|
*/
|
|
223
|
-
export const isRemoved = (segmentLike: unknown): segmentLike is
|
|
204
|
+
// export const isRemoved = (segmentLike: unknown): segmentLike is IHasRemovalInfo =>
|
|
205
|
+
// toRemovalInfo(segmentLike) !== undefined;
|
|
206
|
+
|
|
207
|
+
export const isRemoved = (segmentLike: unknown): segmentLike is IHasRemovalInfo =>
|
|
224
208
|
toRemovalInfo(segmentLike) !== undefined;
|
|
225
209
|
|
|
226
210
|
/**
|
|
@@ -229,9 +213,11 @@ export const isRemoved = (segmentLike: unknown): segmentLike is IRemovalInfo =>
|
|
|
229
213
|
* @param segmentLike - The segment-like object to check.
|
|
230
214
|
* @throws Will throw an error if the segment does not have removal info.
|
|
231
215
|
*/
|
|
232
|
-
export const assertRemoved: <T extends Partial<
|
|
233
|
-
segmentLike: ISegmentInternal | Partial<
|
|
234
|
-
) => asserts segmentLike is
|
|
216
|
+
export const assertRemoved: <T extends Partial<IHasRemovalInfo> | undefined>(
|
|
217
|
+
segmentLike: ISegmentInternal | Partial<IHasRemovalInfo> | T,
|
|
218
|
+
) => asserts segmentLike is IHasRemovalInfo | Exclude<T, Partial<IHasRemovalInfo>> = (
|
|
219
|
+
segmentLike,
|
|
220
|
+
) =>
|
|
235
221
|
assert(segmentLike === undefined || isRemoved(segmentLike), 0xaa2 /* must be removalInfo */);
|
|
236
222
|
|
|
237
223
|
/**
|
|
@@ -241,83 +227,15 @@ export const assertRemoved: <T extends Partial<IRemovalInfo> | undefined>(
|
|
|
241
227
|
* ensures no further usage of the removed removal info is allowed. if continued use is required other
|
|
242
228
|
* type coercion methods should be use to correctly re-type the variable.
|
|
243
229
|
*/
|
|
244
|
-
export const removeRemovalInfo: (nodeLike:
|
|
230
|
+
export const removeRemovalInfo: (nodeLike: IHasRemovalInfo) => asserts nodeLike is never = (
|
|
245
231
|
nodeLike,
|
|
246
232
|
) =>
|
|
247
|
-
Object.assign<
|
|
248
|
-
|
|
249
|
-
removedClientIds: undefined,
|
|
250
|
-
removedSeq: undefined,
|
|
233
|
+
Object.assign<IHasRemovalInfo, Record<keyof IHasRemovalInfo, undefined>>(nodeLike, {
|
|
234
|
+
removes: undefined,
|
|
251
235
|
});
|
|
252
236
|
|
|
253
237
|
/**
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
* Note that merge-tree does not currently support moving and only supports
|
|
257
|
-
* obliterate. The fields below include "move" in their names to avoid renaming
|
|
258
|
-
* in the future, when moves _are_ supported.
|
|
259
|
-
*/
|
|
260
|
-
export interface IMoveInfo {
|
|
261
|
-
/**
|
|
262
|
-
* Local seq at which this segment was moved if the move is yet-to-be
|
|
263
|
-
* acked.
|
|
264
|
-
*/
|
|
265
|
-
localMovedSeq?: number;
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* The first seq at which this segment was moved.
|
|
269
|
-
*/
|
|
270
|
-
movedSeq: number;
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* All seqs at which this segment was moved. In the case of overlapping,
|
|
274
|
-
* concurrent moves this array will contain multiple seqs.
|
|
275
|
-
*
|
|
276
|
-
* The seq at `movedSeqs[i]` corresponds to the client id at `movedClientIds[i]`.
|
|
277
|
-
*
|
|
278
|
-
* The first element corresponds to the seq of the first move
|
|
279
|
-
*/
|
|
280
|
-
movedSeqs: number[];
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* A reference to the inserted destination segment corresponding to this
|
|
284
|
-
* segment's move.
|
|
285
|
-
*
|
|
286
|
-
* If undefined, the move was an obliterate.
|
|
287
|
-
*
|
|
288
|
-
* Currently this field is unused, as we only support obliterate operations
|
|
289
|
-
*/
|
|
290
|
-
moveDst?: ReferencePosition;
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* List of client IDs that have moved this segment.
|
|
294
|
-
*
|
|
295
|
-
* The client that actually moved the segment (i.e. whose move op was sequenced
|
|
296
|
-
* first) is stored as the first client in this list. Other clients in the
|
|
297
|
-
* list have all issued concurrent ops to move the segment.
|
|
298
|
-
*/
|
|
299
|
-
movedClientIds: number[];
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export const toMoveInfo = (segmentLike: unknown): IMoveInfo | undefined =>
|
|
303
|
-
hasProp(segmentLike, "movedClientIds", "array") &&
|
|
304
|
-
hasProp(segmentLike, "movedSeq", "number") &&
|
|
305
|
-
hasProp(segmentLike, "movedSeqs", "array")
|
|
306
|
-
? segmentLike
|
|
307
|
-
: undefined;
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* A type-guard which determines if the segment has move info, and
|
|
311
|
-
* returns true if it does, along with applying strong typing.
|
|
312
|
-
*
|
|
313
|
-
* @param segmentLike - The segment-like object to check.
|
|
314
|
-
* @returns True if the segment has move info, otherwise false.
|
|
315
|
-
*/
|
|
316
|
-
export const isMoved = (segmentLike: unknown): segmentLike is IMoveInfo =>
|
|
317
|
-
toMoveInfo(segmentLike) !== undefined;
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Returns whether this segment was marked moved as soon as its insertion was acked.
|
|
238
|
+
* Returns whether this segment was marked removed as soon as its insertion was acked.
|
|
321
239
|
*
|
|
322
240
|
* This can happen when an an insert occurs concurrent to an obliterate over the range the segment was inserted into,
|
|
323
241
|
* and the obliterate was sequenced first.
|
|
@@ -325,32 +243,21 @@ export const isMoved = (segmentLike: unknown): segmentLike is IMoveInfo =>
|
|
|
325
243
|
* When this happens, the segment is only ever visible to the client that inserted the segment
|
|
326
244
|
* (and only until that client has seen the obliterate which removed their segment).
|
|
327
245
|
*/
|
|
328
|
-
export function
|
|
329
|
-
const
|
|
330
|
-
const
|
|
331
|
-
if (
|
|
246
|
+
export function wasRemovedOnInsert(segment: IHasInsertionInfo & ISegmentPrivate): boolean {
|
|
247
|
+
const removeInfo = toRemovalInfo(segment);
|
|
248
|
+
const removedSeq = removeInfo?.removes[0].seq;
|
|
249
|
+
if (removedSeq === undefined || removedSeq === UnassignedSequenceNumber) {
|
|
332
250
|
return false;
|
|
333
251
|
}
|
|
334
252
|
|
|
335
|
-
const insertSeq = segment.seq;
|
|
336
|
-
return insertSeq === UnassignedSequenceNumber || insertSeq >
|
|
253
|
+
const insertSeq = segment.insert.seq;
|
|
254
|
+
return insertSeq === UnassignedSequenceNumber || insertSeq > removedSeq;
|
|
337
255
|
}
|
|
338
256
|
|
|
339
|
-
/**
|
|
340
|
-
* Asserts that the segment has move info. Usage of this function should not produce a user facing error.
|
|
341
|
-
*
|
|
342
|
-
* @param segmentLike - The segment-like object to check.
|
|
343
|
-
* @throws Will throw an error if the segment does not have move info.
|
|
344
|
-
*/
|
|
345
|
-
export const assertMoved: <T extends Partial<IMoveInfo> | undefined>(
|
|
346
|
-
segmentLike: ISegmentInternal | Partial<IMoveInfo> | T,
|
|
347
|
-
) => asserts segmentLike is IMoveInfo | Exclude<T, Partial<IMoveInfo>> = (segmentLike) =>
|
|
348
|
-
assert(segmentLike === undefined || isMoved(segmentLike), 0xaa3 /* must be moveInfo */);
|
|
349
|
-
|
|
350
257
|
/**
|
|
351
258
|
* A union type representing any segment info.
|
|
352
259
|
*/
|
|
353
|
-
export type SegmentInfo = IMergeNodeInfo |
|
|
260
|
+
export type SegmentInfo = IMergeNodeInfo | IHasInsertionInfo | IHasRemovalInfo;
|
|
354
261
|
|
|
355
262
|
/**
|
|
356
263
|
* A type representing a segment with additional info.
|
|
@@ -14,20 +14,6 @@ import type {
|
|
|
14
14
|
} from "./ops.js";
|
|
15
15
|
import { MapLike, PropertySet, clone, createMap } from "./properties.js";
|
|
16
16
|
|
|
17
|
-
/**
|
|
18
|
-
* @internal
|
|
19
|
-
*/
|
|
20
|
-
export enum PropertiesRollback {
|
|
21
|
-
/**
|
|
22
|
-
* Not in a rollback
|
|
23
|
-
*/
|
|
24
|
-
None,
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Rollback
|
|
28
|
-
*/
|
|
29
|
-
Rollback,
|
|
30
|
-
}
|
|
31
17
|
/**
|
|
32
18
|
* Minimally copies properties and the property manager from source to destination.
|
|
33
19
|
* @internal
|
|
@@ -203,9 +189,9 @@ export class PropertiesManager {
|
|
|
203
189
|
seq: number,
|
|
204
190
|
msn: number,
|
|
205
191
|
collaborating: boolean = false,
|
|
206
|
-
rollback:
|
|
192
|
+
rollback: boolean = false,
|
|
207
193
|
): MapLike<unknown> {
|
|
208
|
-
if (rollback
|
|
194
|
+
if (rollback) {
|
|
209
195
|
return this.rollbackProperties(op, seg, collaborating);
|
|
210
196
|
}
|
|
211
197
|
const rtn = applyChanges(op, seg, seq, (properties, deltas, key, value) => {
|
package/src/snapshotLoader.ts
CHANGED
|
@@ -25,11 +25,11 @@ import { NonCollabClient, UniversalSequenceNumber } from "./constants.js";
|
|
|
25
25
|
import { MergeTree } from "./mergeTree.js";
|
|
26
26
|
import { ISegmentPrivate } from "./mergeTreeNodes.js";
|
|
27
27
|
import { IJSONSegment } from "./ops.js";
|
|
28
|
+
import { PriorPerspective } from "./perspective.js";
|
|
28
29
|
import {
|
|
29
|
-
|
|
30
|
+
IHasRemovalInfo,
|
|
30
31
|
overwriteInfo,
|
|
31
|
-
type
|
|
32
|
-
type IMoveInfo,
|
|
32
|
+
type IHasInsertionInfo,
|
|
33
33
|
type SegmentWithInfo,
|
|
34
34
|
} from "./segmentInfos.js";
|
|
35
35
|
import {
|
|
@@ -39,6 +39,8 @@ import {
|
|
|
39
39
|
} from "./snapshotChunks.js";
|
|
40
40
|
import { SnapshotV1 } from "./snapshotV1.js";
|
|
41
41
|
import { SnapshotLegacy } from "./snapshotlegacy.js";
|
|
42
|
+
import type { RemoveOperationStamp } from "./stamps.js";
|
|
43
|
+
import * as opstampUtils from "./stamps.js";
|
|
42
44
|
|
|
43
45
|
export class SnapshotLoader {
|
|
44
46
|
private readonly logger: ITelemetryLoggerExt;
|
|
@@ -102,16 +104,21 @@ export class SnapshotLoader {
|
|
|
102
104
|
|
|
103
105
|
private readonly specToSegment = (
|
|
104
106
|
spec: IJSONSegment | IJSONSegmentWithMergeInfo,
|
|
105
|
-
): SegmentWithInfo<
|
|
107
|
+
): SegmentWithInfo<IHasInsertionInfo> => {
|
|
106
108
|
if (hasMergeInfo(spec)) {
|
|
107
|
-
const seg = overwriteInfo<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
const seg = overwriteInfo<IHasInsertionInfo>(this.client.specToSegment(spec.json), {
|
|
110
|
+
insert: {
|
|
111
|
+
type: "insert",
|
|
112
|
+
seq: spec.seq ?? UniversalSequenceNumber,
|
|
113
|
+
clientId:
|
|
114
|
+
spec.client === undefined
|
|
115
|
+
? NonCollabClient
|
|
116
|
+
: this.client.getOrAddShortClientId(spec.client),
|
|
117
|
+
},
|
|
113
118
|
});
|
|
114
119
|
|
|
120
|
+
const removes: RemoveOperationStamp[] = [];
|
|
121
|
+
|
|
115
122
|
if (spec.removedSeq !== undefined) {
|
|
116
123
|
// this format had a bug where it didn't store all the overlap clients
|
|
117
124
|
// this is for back compat, so we change the singular id to an array
|
|
@@ -122,32 +129,58 @@ export class SnapshotLoader {
|
|
|
122
129
|
spec.removedClientIds ??= [specAsBuggyFormat.removedClient];
|
|
123
130
|
}
|
|
124
131
|
assert(spec.removedClientIds !== undefined, 0xaac /* must have removedClient ids */);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
const firstRemovedSeq = spec.removedSeq;
|
|
133
|
+
// TODO:AB#32299: To correctly support perspectives from other clients which don't assume they have seen
|
|
134
|
+
// all ops, we need to actually record these in the summary. For now we use fake data, and it turns
|
|
135
|
+
// out ok since none of these values end up being used. (specifically, the 'firstRemovedSeq' is fake
|
|
136
|
+
// for all values other than the actual first remove).
|
|
137
|
+
// This issue only affects V1 summaries, as the strategy in snapshotlegacy avoids storing merge info directly.
|
|
138
|
+
removes.push(
|
|
139
|
+
...spec.removedClientIds.map(
|
|
140
|
+
(id) =>
|
|
141
|
+
({
|
|
142
|
+
type: "setRemove",
|
|
143
|
+
seq: firstRemovedSeq,
|
|
144
|
+
clientId: this.client.getOrAddShortClientId(id),
|
|
145
|
+
}) as const,
|
|
129
146
|
),
|
|
130
|
-
|
|
147
|
+
);
|
|
131
148
|
}
|
|
132
149
|
if (spec.movedSeq !== undefined) {
|
|
133
150
|
assert(
|
|
134
151
|
spec.movedClientIds !== undefined && spec.movedSeqs !== undefined,
|
|
135
152
|
0xaa5 /* must have movedIds ids */,
|
|
136
153
|
);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
154
|
+
assert(
|
|
155
|
+
spec.movedClientIds.length === spec.movedSeqs.length,
|
|
156
|
+
0xb5f /* Expected same length for client ids and seqs */,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
removes.push(
|
|
160
|
+
...spec.movedClientIds.map(
|
|
161
|
+
(id, i) =>
|
|
162
|
+
({
|
|
163
|
+
type: "sliceRemove",
|
|
164
|
+
seq: spec.movedSeqs![i],
|
|
165
|
+
clientId: this.client.getOrAddShortClientId(id),
|
|
166
|
+
}) as const,
|
|
142
167
|
),
|
|
143
|
-
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (removes.length > 0) {
|
|
172
|
+
removes.sort(opstampUtils.compare);
|
|
173
|
+
overwriteInfo<IHasRemovalInfo>(seg, { removes });
|
|
144
174
|
}
|
|
145
175
|
|
|
146
176
|
return seg;
|
|
147
177
|
}
|
|
148
178
|
return overwriteInfo(this.client.specToSegment(spec), {
|
|
149
|
-
|
|
150
|
-
|
|
179
|
+
insert: {
|
|
180
|
+
type: "insert",
|
|
181
|
+
seq: UniversalSequenceNumber,
|
|
182
|
+
clientId: NonCollabClient,
|
|
183
|
+
},
|
|
151
184
|
});
|
|
152
185
|
};
|
|
153
186
|
|
|
@@ -206,7 +239,7 @@ export class SnapshotLoader {
|
|
|
206
239
|
}
|
|
207
240
|
|
|
208
241
|
let chunksWithAttribution = chunk1.attribution === undefined ? 0 : 1;
|
|
209
|
-
const segs: SegmentWithInfo<
|
|
242
|
+
const segs: SegmentWithInfo<IHasInsertionInfo>[] = [];
|
|
210
243
|
let lengthSofar = chunk1.length;
|
|
211
244
|
for (
|
|
212
245
|
let chunkIndex = 1;
|
|
@@ -243,19 +276,18 @@ export class SnapshotLoader {
|
|
|
243
276
|
|
|
244
277
|
// Helper to insert segments at the end of the MergeTree.
|
|
245
278
|
const mergeTree = this.mergeTree;
|
|
246
|
-
const append = (segments: ISegmentPrivate[],
|
|
279
|
+
const append = (segments: ISegmentPrivate[], clientId: number, seq: number): void => {
|
|
247
280
|
mergeTree.insertSegments(
|
|
248
281
|
mergeTree.root.cachedLength ?? 0,
|
|
249
282
|
segments,
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
seq,
|
|
283
|
+
new PriorPerspective(UniversalSequenceNumber, clientId),
|
|
284
|
+
{ seq, clientId },
|
|
253
285
|
undefined,
|
|
254
286
|
);
|
|
255
287
|
};
|
|
256
288
|
|
|
257
289
|
// Helpers to batch-insert segments that are below the min seq
|
|
258
|
-
const batch: SegmentWithInfo<
|
|
290
|
+
const batch: SegmentWithInfo<IHasInsertionInfo>[] = [];
|
|
259
291
|
const flushBatch = (): void => {
|
|
260
292
|
if (batch.length > 0) {
|
|
261
293
|
append(batch, NonCollabClient, UniversalSequenceNumber);
|
|
@@ -263,7 +295,7 @@ export class SnapshotLoader {
|
|
|
263
295
|
};
|
|
264
296
|
|
|
265
297
|
for (const seg of segs) {
|
|
266
|
-
const { clientId, seq } = seg;
|
|
298
|
+
const { clientId, seq } = seg.insert;
|
|
267
299
|
// If the segment can be batch inserted, add it to the 'batch' array. Otherwise, flush
|
|
268
300
|
// any batched segments and then insert the current segment individually.
|
|
269
301
|
if (clientId === NonCollabClient && seq === UniversalSequenceNumber) {
|
package/src/snapshotV1.ts
CHANGED
|
@@ -19,13 +19,13 @@ import {
|
|
|
19
19
|
} from "@fluidframework/telemetry-utils/internal";
|
|
20
20
|
|
|
21
21
|
import { IAttributionCollection } from "./attributionCollection.js";
|
|
22
|
-
import {
|
|
22
|
+
import { NonCollabClient } from "./constants.js";
|
|
23
23
|
import { MergeTree } from "./mergeTree.js";
|
|
24
24
|
import { walkAllChildSegments } from "./mergeTreeNodeWalk.js";
|
|
25
25
|
import { ISegmentPrivate } from "./mergeTreeNodes.js";
|
|
26
26
|
import type { IJSONSegment } from "./ops.js";
|
|
27
27
|
import { PropertySet, matchProperties } from "./properties.js";
|
|
28
|
-
import { assertInserted,
|
|
28
|
+
import { assertInserted, isRemoved } from "./segmentInfos.js";
|
|
29
29
|
import {
|
|
30
30
|
IJSONSegmentWithMergeInfo,
|
|
31
31
|
JsonSegmentSpecs,
|
|
@@ -36,6 +36,8 @@ import {
|
|
|
36
36
|
type VersionedMergeTreeChunk,
|
|
37
37
|
} from "./snapshotChunks.js";
|
|
38
38
|
import { SnapshotLegacy } from "./snapshotlegacy.js";
|
|
39
|
+
import type { OperationStamp } from "./stamps.js";
|
|
40
|
+
import * as opstampUtils from "./stamps.js";
|
|
39
41
|
|
|
40
42
|
export class SnapshotV1 {
|
|
41
43
|
// Split snapshot into two entries - headers (small) and body (overflow) for faster loading initial content
|
|
@@ -190,6 +192,7 @@ export class SnapshotV1 {
|
|
|
190
192
|
extractSync(): JsonSegmentSpecs[] {
|
|
191
193
|
const mergeTree = this.mergeTree;
|
|
192
194
|
const minSeq = this.header.minSequenceNumber;
|
|
195
|
+
const minSeqStamp: OperationStamp = { seq: minSeq, clientId: NonCollabClient };
|
|
193
196
|
|
|
194
197
|
let originalSegments = 0;
|
|
195
198
|
let segmentsAfterCombine = 0;
|
|
@@ -232,11 +235,10 @@ export class SnapshotV1 {
|
|
|
232
235
|
// b) The segment was removed at or below the MSN. Pending ops can no longer reference this
|
|
233
236
|
// segment, and therefore we can discard it.
|
|
234
237
|
if (
|
|
235
|
-
segment.
|
|
236
|
-
(isRemoved(segment) && segment.
|
|
237
|
-
(isMoved(segment) && segment.movedSeq <= minSeq)
|
|
238
|
+
opstampUtils.isLocal(segment.insert) ||
|
|
239
|
+
(isRemoved(segment) && opstampUtils.lte(segment.removes[0], minSeqStamp))
|
|
238
240
|
) {
|
|
239
|
-
if (segment.
|
|
241
|
+
if (opstampUtils.isAcked(segment.insert)) {
|
|
240
242
|
originalSegments += 1;
|
|
241
243
|
}
|
|
242
244
|
return true;
|
|
@@ -248,10 +250,8 @@ export class SnapshotV1 {
|
|
|
248
250
|
// (seq, client, etc.) This information is only needed if the segment is above the MSN (and doesn't
|
|
249
251
|
// have a pending remove.)
|
|
250
252
|
if (
|
|
251
|
-
segment.
|
|
252
|
-
(!isRemoved(segment) ||
|
|
253
|
-
segment.removedSeq === UnassignedSequenceNumber) && // .. Removal op to be delivered on reconnect
|
|
254
|
-
(!isMoved(segment) || segment.movedSeq === UnassignedSequenceNumber)
|
|
253
|
+
opstampUtils.lte(segment.insert, minSeqStamp) && // Segment is below the MSN, and...
|
|
254
|
+
(!isRemoved(segment) || opstampUtils.isLocal(segment.removes[0]))
|
|
255
255
|
) {
|
|
256
256
|
// This segment is below the MSN, which means that future ops will not reference it. Attempt to
|
|
257
257
|
// coalesce the new segment with the previous (if any).
|
|
@@ -285,38 +285,46 @@ export class SnapshotV1 {
|
|
|
285
285
|
json: segment.toJSONObject() as IJSONSegment,
|
|
286
286
|
};
|
|
287
287
|
// If the segment insertion is above the MSN, record the insertion merge info.
|
|
288
|
-
if (segment.
|
|
289
|
-
raw.seq = segment.seq;
|
|
290
|
-
raw.client = this.getLongClientId(segment.clientId);
|
|
288
|
+
if (opstampUtils.greaterThan(segment.insert, minSeqStamp)) {
|
|
289
|
+
raw.seq = segment.insert.seq;
|
|
290
|
+
raw.client = this.getLongClientId(segment.insert.clientId);
|
|
291
291
|
}
|
|
292
|
+
|
|
292
293
|
// We have already dispensed with removed segments below the MSN and removed segments with unassigned
|
|
293
294
|
// sequence numbers. Any remaining removal info should be preserved.
|
|
294
|
-
if (isRemoved(segment)) {
|
|
295
|
+
if (isRemoved(segment) && segment.removes.some((r) => r.type === "setRemove")) {
|
|
296
|
+
const removes = segment.removes.filter((r) => r.type === "setRemove");
|
|
297
|
+
const firstRemove = removes[0];
|
|
295
298
|
assert(
|
|
296
|
-
|
|
299
|
+
opstampUtils.isAcked(firstRemove) &&
|
|
300
|
+
opstampUtils.greaterThan(firstRemove, minSeqStamp),
|
|
297
301
|
0x065 /* "On removal info preservation, segment has invalid removed sequence number!" */,
|
|
298
302
|
);
|
|
299
|
-
|
|
303
|
+
// TODO:AB#32299 By not preserving sequence numbers other than the first move,
|
|
304
|
+
// We drop data here which will be necessary to allow perspectives of remote clients that
|
|
305
|
+
// don't include all of their ops. This should be remedied at some point.
|
|
306
|
+
raw.removedSeq = firstRemove.seq;
|
|
300
307
|
|
|
301
308
|
// back compat for when we split overlap and removed client
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
: this.getLongClientId(segment.removedClientIds[0]);
|
|
309
|
+
// This can be removed when we can safely assume no clients running Fluid packages <= 0.58 will ever
|
|
310
|
+
// load a document produced by a version beyond the removal. It is vestigial in the meantime.
|
|
311
|
+
raw.removedClient = this.getLongClientId(firstRemove.clientId);
|
|
306
312
|
|
|
307
|
-
raw.removedClientIds =
|
|
308
|
-
this.getLongClientId(id),
|
|
309
|
-
);
|
|
313
|
+
raw.removedClientIds = removes.map(({ clientId }) => this.getLongClientId(clientId));
|
|
310
314
|
}
|
|
311
315
|
|
|
312
|
-
if (
|
|
316
|
+
if (isRemoved(segment) && segment.removes.some((r) => r.type === "sliceRemove")) {
|
|
317
|
+
// In this format, we used the term "move" to refer to a sliceRemove/obliterate.
|
|
318
|
+
const moves = segment.removes.filter((r) => r.type === "sliceRemove");
|
|
319
|
+
const firstMove = moves[0];
|
|
313
320
|
assert(
|
|
314
|
-
|
|
321
|
+
opstampUtils.isAcked(firstMove) &&
|
|
322
|
+
opstampUtils.greaterThan(firstMove, minSeqStamp),
|
|
315
323
|
0x873 /* On move info preservation, segment has invalid moved sequence number! */,
|
|
316
324
|
);
|
|
317
|
-
raw.movedSeq =
|
|
318
|
-
raw.movedSeqs =
|
|
319
|
-
raw.movedClientIds =
|
|
325
|
+
raw.movedSeq = firstMove.seq;
|
|
326
|
+
raw.movedSeqs = moves.map(({ seq }) => seq);
|
|
327
|
+
raw.movedClientIds = moves.map(({ clientId }) => this.getLongClientId(clientId));
|
|
320
328
|
}
|
|
321
329
|
|
|
322
330
|
// Sanity check that we are preserving either the seq > minSeq or a (re)moved segment's info.
|