@fluidframework/merge-tree 2.12.0 → 2.13.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 +4 -0
- package/dist/MergeTreeTextHelper.d.ts.map +1 -1
- package/dist/MergeTreeTextHelper.js +0 -2
- package/dist/MergeTreeTextHelper.js.map +1 -1
- package/dist/attributionPolicy.d.ts.map +1 -1
- package/dist/attributionPolicy.js +6 -16
- package/dist/attributionPolicy.js.map +1 -1
- package/dist/client.d.ts +3 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +39 -28
- package/dist/client.js.map +1 -1
- package/dist/endOfTreeSegment.d.ts +2 -1
- package/dist/endOfTreeSegment.d.ts.map +1 -1
- package/dist/endOfTreeSegment.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/index.js.map +1 -1
- package/dist/mergeTree.d.ts +8 -7
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +190 -216
- package/dist/mergeTree.js.map +1 -1
- package/dist/mergeTreeNodeWalk.d.ts.map +1 -1
- package/dist/mergeTreeNodeWalk.js +3 -2
- package/dist/mergeTreeNodeWalk.js.map +1 -1
- package/dist/mergeTreeNodes.d.ts +76 -162
- package/dist/mergeTreeNodes.d.ts.map +1 -1
- package/dist/mergeTreeNodes.js +100 -112
- package/dist/mergeTreeNodes.js.map +1 -1
- package/dist/mergeTreeTracking.d.ts.map +1 -1
- package/dist/mergeTreeTracking.js +0 -2
- package/dist/mergeTreeTracking.js.map +1 -1
- package/dist/partialLengths.d.ts +2 -2
- package/dist/partialLengths.d.ts.map +1 -1
- package/dist/partialLengths.js +28 -26
- package/dist/partialLengths.js.map +1 -1
- package/dist/perspective.d.ts +3 -2
- package/dist/perspective.d.ts.map +1 -1
- package/dist/perspective.js +11 -4
- package/dist/perspective.js.map +1 -1
- package/dist/referencePositions.d.ts.map +1 -1
- package/dist/referencePositions.js +4 -1
- package/dist/referencePositions.js.map +1 -1
- package/dist/revertibles.d.ts.map +1 -1
- package/dist/revertibles.js +10 -11
- package/dist/revertibles.js.map +1 -1
- package/dist/segmentGroupCollection.d.ts +4 -4
- package/dist/segmentGroupCollection.d.ts.map +1 -1
- package/dist/segmentGroupCollection.js +0 -6
- package/dist/segmentGroupCollection.js.map +1 -1
- package/dist/segmentInfos.d.ts +257 -0
- package/dist/segmentInfos.d.ts.map +1 -0
- package/dist/segmentInfos.js +166 -0
- package/dist/segmentInfos.js.map +1 -0
- package/dist/snapshotLoader.d.ts.map +1 -1
- package/dist/snapshotLoader.js +38 -44
- package/dist/snapshotLoader.js.map +1 -1
- package/dist/snapshotV1.d.ts.map +1 -1
- package/dist/snapshotV1.js +9 -12
- package/dist/snapshotV1.js.map +1 -1
- package/dist/snapshotlegacy.d.ts +2 -2
- package/dist/snapshotlegacy.d.ts.map +1 -1
- package/dist/snapshotlegacy.js +5 -3
- package/dist/snapshotlegacy.js.map +1 -1
- package/dist/sortedSegmentSet.d.ts.map +1 -1
- package/dist/sortedSegmentSet.js +5 -8
- package/dist/sortedSegmentSet.js.map +1 -1
- package/dist/test/beastTest.spec.d.ts +0 -2
- package/dist/test/beastTest.spec.d.ts.map +1 -1
- package/dist/test/beastTest.spec.js +1 -5
- package/dist/test/beastTest.spec.js.map +1 -1
- package/dist/test/client.annotateMarker.spec.js.map +1 -1
- package/dist/test/client.applyMsg.spec.js +15 -12
- package/dist/test/client.applyMsg.spec.js.map +1 -1
- package/dist/test/client.attributionFarm.spec.js.map +1 -1
- package/dist/test/client.getPosition.spec.js +3 -2
- package/dist/test/client.getPosition.spec.js.map +1 -1
- package/dist/test/client.localReference.spec.js +6 -6
- package/dist/test/client.localReference.spec.js.map +1 -1
- package/dist/test/client.localReferenceFarm.spec.js.map +1 -1
- package/dist/test/client.rollback.spec.js.map +1 -1
- package/dist/test/dirname.cjs +0 -1
- package/dist/test/dirname.cjs.map +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +2 -4
- package/dist/test/index.js.map +1 -1
- package/dist/test/mergeTree.annotate.spec.js +3 -0
- package/dist/test/mergeTree.annotate.spec.js.map +1 -1
- package/dist/test/mergeTree.insertingWalk.spec.js +1 -1
- package/dist/test/mergeTree.insertingWalk.spec.js.map +1 -1
- package/dist/test/mergeTree.markRangeRemoved.spec.js +2 -0
- package/dist/test/mergeTree.markRangeRemoved.spec.js.map +1 -1
- package/dist/test/mergeTree.walk.spec.js.map +1 -1
- package/dist/test/mergeTreeOperationRunner.d.ts.map +1 -1
- package/dist/test/mergeTreeOperationRunner.js +2 -3
- package/dist/test/mergeTreeOperationRunner.js.map +1 -1
- package/dist/test/obliterate.spec.js.map +1 -1
- package/dist/test/propertyManager.spec.js.map +1 -1
- package/dist/test/reconnectHelper.d.ts +2 -1
- package/dist/test/reconnectHelper.d.ts.map +1 -1
- package/dist/test/reconnectHelper.js.map +1 -1
- package/dist/test/resetPendingSegmentsToOp.spec.js.map +1 -1
- package/dist/test/revertibleFarm.spec.js.map +1 -1
- package/dist/test/segmentGroupCollection.spec.js +15 -3
- package/dist/test/segmentGroupCollection.spec.js.map +1 -1
- package/dist/test/snapshot.utils.d.ts +2 -2
- package/dist/test/snapshot.utils.d.ts.map +1 -1
- package/dist/test/snapshot.utils.js.map +1 -1
- package/dist/test/sortedSegmentSet.spec.js +4 -3
- package/dist/test/sortedSegmentSet.spec.js.map +1 -1
- package/dist/test/testClient.d.ts +8 -6
- package/dist/test/testClient.d.ts.map +1 -1
- package/dist/test/testClient.js +29 -27
- package/dist/test/testClient.js.map +1 -1
- package/dist/test/testClientLogger.d.ts.map +1 -1
- package/dist/test/testClientLogger.js +6 -4
- package/dist/test/testClientLogger.js.map +1 -1
- package/dist/test/testUtils.d.ts +2 -2
- package/dist/test/testUtils.d.ts.map +1 -1
- package/dist/test/testUtils.js +32 -8
- 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 +12 -6
- package/dist/test/text.js.map +1 -1
- package/dist/test/tracking.spec.js.map +1 -1
- package/dist/test/wordUnitTests.spec.js +1 -1
- package/dist/test/wordUnitTests.spec.js.map +1 -1
- package/dist/zamboni.d.ts.map +1 -1
- package/dist/zamboni.js +8 -7
- package/dist/zamboni.js.map +1 -1
- package/lib/MergeTreeTextHelper.d.ts.map +1 -1
- package/lib/MergeTreeTextHelper.js +0 -2
- package/lib/MergeTreeTextHelper.js.map +1 -1
- package/lib/attributionPolicy.d.ts.map +1 -1
- package/lib/attributionPolicy.js +6 -16
- package/lib/attributionPolicy.js.map +1 -1
- package/lib/client.d.ts +3 -4
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +40 -29
- package/lib/client.js.map +1 -1
- package/lib/endOfTreeSegment.d.ts +2 -1
- package/lib/endOfTreeSegment.d.ts.map +1 -1
- package/lib/endOfTreeSegment.js.map +1 -1
- package/lib/index.d.ts +2 -1
- 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 +8 -7
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +177 -205
- package/lib/mergeTree.js.map +1 -1
- package/lib/mergeTreeNodeWalk.d.ts.map +1 -1
- package/lib/mergeTreeNodeWalk.js +3 -2
- package/lib/mergeTreeNodeWalk.js.map +1 -1
- package/lib/mergeTreeNodes.d.ts +76 -162
- package/lib/mergeTreeNodes.d.ts.map +1 -1
- package/lib/mergeTreeNodes.js +95 -108
- package/lib/mergeTreeNodes.js.map +1 -1
- package/lib/mergeTreeTracking.d.ts.map +1 -1
- package/lib/mergeTreeTracking.js +0 -2
- package/lib/mergeTreeTracking.js.map +1 -1
- package/lib/partialLengths.d.ts +2 -2
- package/lib/partialLengths.d.ts.map +1 -1
- package/lib/partialLengths.js +25 -23
- package/lib/partialLengths.js.map +1 -1
- package/lib/perspective.d.ts +3 -2
- package/lib/perspective.d.ts.map +1 -1
- package/lib/perspective.js +11 -4
- package/lib/perspective.js.map +1 -1
- package/lib/referencePositions.d.ts.map +1 -1
- package/lib/referencePositions.js +4 -1
- package/lib/referencePositions.js.map +1 -1
- package/lib/revertibles.d.ts.map +1 -1
- package/lib/revertibles.js +8 -9
- package/lib/revertibles.js.map +1 -1
- package/lib/segmentGroupCollection.d.ts +4 -4
- package/lib/segmentGroupCollection.d.ts.map +1 -1
- package/lib/segmentGroupCollection.js +0 -6
- package/lib/segmentGroupCollection.js.map +1 -1
- package/lib/segmentInfos.d.ts +257 -0
- package/lib/segmentInfos.d.ts.map +1 -0
- package/lib/segmentInfos.js +145 -0
- package/lib/segmentInfos.js.map +1 -0
- package/lib/snapshotLoader.d.ts.map +1 -1
- package/lib/snapshotLoader.js +38 -44
- package/lib/snapshotLoader.js.map +1 -1
- package/lib/snapshotV1.d.ts.map +1 -1
- package/lib/snapshotV1.js +9 -12
- package/lib/snapshotV1.js.map +1 -1
- package/lib/snapshotlegacy.d.ts +2 -2
- package/lib/snapshotlegacy.d.ts.map +1 -1
- package/lib/snapshotlegacy.js +5 -3
- package/lib/snapshotlegacy.js.map +1 -1
- package/lib/sortedSegmentSet.d.ts.map +1 -1
- package/lib/sortedSegmentSet.js +5 -8
- package/lib/sortedSegmentSet.js.map +1 -1
- package/lib/test/beastTest.spec.d.ts +0 -2
- package/lib/test/beastTest.spec.d.ts.map +1 -1
- package/lib/test/beastTest.spec.js +0 -3
- package/lib/test/beastTest.spec.js.map +1 -1
- package/lib/test/client.annotateMarker.spec.js.map +1 -1
- package/lib/test/client.applyMsg.spec.js +15 -12
- package/lib/test/client.applyMsg.spec.js.map +1 -1
- package/lib/test/client.attributionFarm.spec.js.map +1 -1
- package/lib/test/client.getPosition.spec.js +3 -2
- 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.localReferenceFarm.spec.js.map +1 -1
- package/lib/test/client.rollback.spec.js +1 -1
- package/lib/test/client.rollback.spec.js.map +1 -1
- package/lib/test/dirname.cjs +0 -1
- package/lib/test/dirname.cjs.map +1 -1
- package/lib/test/index.d.ts +1 -1
- 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.spec.js +3 -0
- package/lib/test/mergeTree.annotate.spec.js.map +1 -1
- package/lib/test/mergeTree.insertingWalk.spec.js +2 -2
- package/lib/test/mergeTree.insertingWalk.spec.js.map +1 -1
- package/lib/test/mergeTree.markRangeRemoved.spec.js +2 -0
- package/lib/test/mergeTree.markRangeRemoved.spec.js.map +1 -1
- package/lib/test/mergeTree.walk.spec.js.map +1 -1
- package/lib/test/mergeTreeOperationRunner.d.ts.map +1 -1
- package/lib/test/mergeTreeOperationRunner.js +1 -2
- package/lib/test/mergeTreeOperationRunner.js.map +1 -1
- package/lib/test/obliterate.spec.js.map +1 -1
- package/lib/test/propertyManager.spec.js.map +1 -1
- package/lib/test/reconnectHelper.d.ts +2 -1
- package/lib/test/reconnectHelper.d.ts.map +1 -1
- package/lib/test/reconnectHelper.js.map +1 -1
- package/lib/test/resetPendingSegmentsToOp.spec.js.map +1 -1
- package/lib/test/revertibleFarm.spec.js.map +1 -1
- package/lib/test/segmentGroupCollection.spec.js +15 -3
- package/lib/test/segmentGroupCollection.spec.js.map +1 -1
- package/lib/test/snapshot.utils.d.ts +2 -2
- package/lib/test/snapshot.utils.d.ts.map +1 -1
- package/lib/test/snapshot.utils.js.map +1 -1
- package/lib/test/sortedSegmentSet.spec.js +4 -3
- package/lib/test/sortedSegmentSet.spec.js.map +1 -1
- package/lib/test/testClient.d.ts +8 -6
- package/lib/test/testClient.d.ts.map +1 -1
- package/lib/test/testClient.js +30 -28
- package/lib/test/testClient.js.map +1 -1
- package/lib/test/testClientLogger.d.ts.map +1 -1
- package/lib/test/testClientLogger.js +5 -3
- package/lib/test/testClientLogger.js.map +1 -1
- package/lib/test/testUtils.d.ts +2 -2
- package/lib/test/testUtils.d.ts.map +1 -1
- package/lib/test/testUtils.js +9 -8
- 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 +12 -6
- package/lib/test/text.js.map +1 -1
- package/lib/test/tracking.spec.js.map +1 -1
- package/lib/test/wordUnitTests.spec.js +1 -1
- package/lib/test/wordUnitTests.spec.js.map +1 -1
- package/lib/zamboni.d.ts.map +1 -1
- package/lib/zamboni.js +7 -6
- package/lib/zamboni.js.map +1 -1
- package/package.json +17 -17
- package/src/MergeTreeTextHelper.ts +2 -4
- package/src/attributionPolicy.ts +5 -13
- package/src/client.ts +55 -44
- package/src/endOfTreeSegment.ts +3 -3
- package/src/index.ts +4 -6
- package/src/mergeTree.ts +245 -282
- package/src/mergeTreeNodeWalk.ts +3 -2
- package/src/mergeTreeNodes.ts +190 -322
- package/src/mergeTreeTracking.ts +0 -3
- package/src/partialLengths.ts +42 -27
- package/src/perspective.ts +27 -4
- package/src/referencePositions.ts +4 -1
- package/src/revertibles.ts +19 -13
- package/src/segmentGroupCollection.ts +7 -18
- package/src/segmentInfos.ts +377 -0
- package/src/snapshotLoader.ts +60 -57
- package/src/snapshotV1.ts +14 -16
- package/src/snapshotlegacy.ts +12 -17
- package/src/sortedSegmentSet.ts +6 -8
- package/src/zamboni.ts +10 -12
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { assert, isObject } from "@fluidframework/core-utils/internal";
|
|
7
|
+
|
|
8
|
+
import { ISegmentInternal, ISegmentPrivate, MergeBlock } from "./mergeTreeNodes.js";
|
|
9
|
+
import type { ReferencePosition } from "./referencePositions.js";
|
|
10
|
+
|
|
11
|
+
export interface StringToType {
|
|
12
|
+
"string": string;
|
|
13
|
+
"number": number;
|
|
14
|
+
"object": object;
|
|
15
|
+
"array": [];
|
|
16
|
+
"boolean": boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function propExists<P extends string>(
|
|
20
|
+
thing: unknown,
|
|
21
|
+
prop: P,
|
|
22
|
+
): thing is Record<P, unknown> {
|
|
23
|
+
return isObject(thing) && prop in thing;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function hasProp<P extends string, T extends keyof StringToType>(
|
|
27
|
+
thing: unknown,
|
|
28
|
+
prop: P,
|
|
29
|
+
type: T,
|
|
30
|
+
): thing is Record<P, StringToType[typeof type]> {
|
|
31
|
+
return (
|
|
32
|
+
propExists(thing, prop) &&
|
|
33
|
+
(type === "array" ? Array.isArray(thing[prop]) : typeof thing[prop] === type)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function propInstanceOf<P extends string, T>(
|
|
38
|
+
thing: unknown,
|
|
39
|
+
prop: P,
|
|
40
|
+
type: new (...args: any[]) => T,
|
|
41
|
+
): thing is Record<P, T> {
|
|
42
|
+
return propExists(thing, prop) && thing[prop] instanceof type;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Contains insertion information associated to an {@link ISegment}.
|
|
47
|
+
*/
|
|
48
|
+
export interface IInsertionInfo {
|
|
49
|
+
/**
|
|
50
|
+
* Short clientId for the client that inserted this segment.
|
|
51
|
+
*/
|
|
52
|
+
clientId: number;
|
|
53
|
+
/**
|
|
54
|
+
* Local seq at which this segment was inserted.
|
|
55
|
+
* This is defined if and only if the insertion of the segment is pending ack, i.e. `seq` is UnassignedSequenceNumber.
|
|
56
|
+
* Once the segment is acked, this field is cleared.
|
|
57
|
+
*
|
|
58
|
+
* @privateRemarks
|
|
59
|
+
* See {@link CollaborationWindow.localSeq} for more information on the semantics of localSeq.
|
|
60
|
+
*/
|
|
61
|
+
localSeq?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Seq at which this segment was inserted.
|
|
64
|
+
* If undefined, it is assumed the segment was inserted prior to the collab window's minimum sequence number.
|
|
65
|
+
*/
|
|
66
|
+
seq: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Converts a segment-like object to an insertion info object if possible.
|
|
71
|
+
*
|
|
72
|
+
* @param segmentLike - The segment-like object to convert.
|
|
73
|
+
* @returns The insertion info object if the conversion is possible, otherwise undefined.
|
|
74
|
+
*/
|
|
75
|
+
export const toInsertionInfo = (segmentLike: unknown): IInsertionInfo | undefined =>
|
|
76
|
+
hasProp(segmentLike, "clientId", "number") && hasProp(segmentLike, "seq", "number")
|
|
77
|
+
? segmentLike
|
|
78
|
+
: undefined;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A type-guard which determines if the segment has insertion info, and
|
|
82
|
+
* returns true if it does, along with applying strong typing.
|
|
83
|
+
*
|
|
84
|
+
* @param segmentLike - The segment-like object to check.
|
|
85
|
+
* @returns True if the segment has insertion info, otherwise false.
|
|
86
|
+
*/
|
|
87
|
+
export const isInserted = (segmentLike: unknown): segmentLike is IInsertionInfo =>
|
|
88
|
+
toInsertionInfo(segmentLike) !== undefined;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Asserts that the segment has insertion info. Usage of this function should not produce a user facing error.
|
|
92
|
+
*
|
|
93
|
+
* @param segmentLike - The segment-like object to check.
|
|
94
|
+
* @throws Will throw an error if the segment does not have insertion info.
|
|
95
|
+
*/
|
|
96
|
+
export const assertInserted: <T extends Partial<IInsertionInfo> | undefined>(
|
|
97
|
+
segmentLike: ISegmentInternal | Partial<IInsertionInfo> | T,
|
|
98
|
+
) => asserts segmentLike is IInsertionInfo | Exclude<T, Partial<IInsertionInfo>> = (
|
|
99
|
+
segmentLike,
|
|
100
|
+
) =>
|
|
101
|
+
assert(
|
|
102
|
+
segmentLike === undefined || isInserted(segmentLike),
|
|
103
|
+
0xaa0 /* must be insertionInfo */,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Common properties for a node in a merge tree.
|
|
108
|
+
*/
|
|
109
|
+
export interface IMergeNodeInfo {
|
|
110
|
+
/**
|
|
111
|
+
* The parent merge block if the node is parented
|
|
112
|
+
*/
|
|
113
|
+
parent: MergeBlock;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The index of this node in its parent's list of children.
|
|
117
|
+
*/
|
|
118
|
+
index: number;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* A string that can be used for comparing the location of this node to other `MergeNode`s in the same tree.
|
|
122
|
+
* `a.ordinal < b.ordinal` if and only if `a` comes before `b` in a pre-order traversal of the tree.
|
|
123
|
+
*/
|
|
124
|
+
ordinal: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Converts a segment-like object to a merge node info object if possible.
|
|
129
|
+
*
|
|
130
|
+
* @param segmentLike - The segment-like object to convert.
|
|
131
|
+
* @returns The merge node info object if the conversion is possible, otherwise undefined.
|
|
132
|
+
*/
|
|
133
|
+
export const toMergeNodeInfo = (nodeLike: unknown): IMergeNodeInfo | undefined =>
|
|
134
|
+
propInstanceOf(nodeLike, "parent", MergeBlock) &&
|
|
135
|
+
hasProp(nodeLike, "ordinal", "string") &&
|
|
136
|
+
hasProp(nodeLike, "index", "number")
|
|
137
|
+
? nodeLike
|
|
138
|
+
: undefined;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* A type-guard which determines if the segment has merge node info, and
|
|
142
|
+
* returns true if it does, along with applying strong typing.
|
|
143
|
+
*
|
|
144
|
+
* @param nodeLike - The segment-like object to check.
|
|
145
|
+
* @returns True if the segment has merge node info, otherwise false.
|
|
146
|
+
*/
|
|
147
|
+
export const isMergeNodeInfo = (nodeLike: unknown): nodeLike is IMergeNodeInfo =>
|
|
148
|
+
toMergeNodeInfo(nodeLike) !== undefined;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Asserts that the segment has merge node info. Usage of this function should not produce a user facing error.
|
|
152
|
+
*
|
|
153
|
+
* @param segmentLike - The segment-like object to check.
|
|
154
|
+
* @throws Will throw an error if the segment does not have merge node info.
|
|
155
|
+
*/
|
|
156
|
+
export const assertMergeNode: <T extends Partial<IMergeNodeInfo> | undefined>(
|
|
157
|
+
nodeLike: ISegmentInternal | ISegmentPrivate | Partial<IMergeNodeInfo> | T,
|
|
158
|
+
) => asserts nodeLike is IMergeNodeInfo | Exclude<T, Partial<IMergeNodeInfo>> = (
|
|
159
|
+
segmentLike,
|
|
160
|
+
) =>
|
|
161
|
+
assert(
|
|
162
|
+
segmentLike === undefined || isMergeNodeInfo(segmentLike),
|
|
163
|
+
0xaa1 /* must be MergeNodeInfo */,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Removes the merge node info. This is used to remove nodes from the merge-tree.
|
|
168
|
+
* @param segmentLike - The segment-like object to check.
|
|
169
|
+
* @returns This function will change the type of the provided node like to never via an assertion. This
|
|
170
|
+
* ensures no further usage of the removed merge node info is allowed. if continued use is required other
|
|
171
|
+
* type coercion methods should be used to correctly re-type the variable.
|
|
172
|
+
*/
|
|
173
|
+
export const removeMergeNodeInfo: (nodeLike: IMergeNodeInfo) => asserts nodeLike is never = (
|
|
174
|
+
nodeLike,
|
|
175
|
+
) =>
|
|
176
|
+
Object.assign<IMergeNodeInfo, Record<keyof IMergeNodeInfo, undefined>>(nodeLike, {
|
|
177
|
+
parent: undefined,
|
|
178
|
+
index: undefined,
|
|
179
|
+
ordinal: undefined,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Contains removal information associated to an {@link ISegment}.
|
|
184
|
+
* @legacy
|
|
185
|
+
* @alpha
|
|
186
|
+
* @deprecated - This interface will be removed in 2.20 with no replacement.
|
|
187
|
+
*/
|
|
188
|
+
export interface IRemovalInfo {
|
|
189
|
+
/**
|
|
190
|
+
* Local seq at which this segment was removed, if the removal is yet-to-be acked.
|
|
191
|
+
*/
|
|
192
|
+
localRemovedSeq?: number;
|
|
193
|
+
/**
|
|
194
|
+
* Seq at which this segment was removed.
|
|
195
|
+
*/
|
|
196
|
+
removedSeq: number;
|
|
197
|
+
/**
|
|
198
|
+
* List of client IDs that have removed this segment.
|
|
199
|
+
* The client that actually removed the segment (i.e. whose removal op was sequenced first) is stored as the first
|
|
200
|
+
* client in this list. Other clients in the list have all issued concurrent ops to remove the segment.
|
|
201
|
+
* @remarks When this list has length \> 1, this is referred to as the "overlapping remove" case.
|
|
202
|
+
*/
|
|
203
|
+
removedClientIds: number[];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Converts a segment-like object to a removal info object if possible.
|
|
208
|
+
*
|
|
209
|
+
* @param segmentLike - The segment-like object to convert.
|
|
210
|
+
* @returns The removal info object if the conversion is possible, otherwise undefined.
|
|
211
|
+
*/
|
|
212
|
+
export const toRemovalInfo = (segmentLike: unknown): IRemovalInfo | undefined =>
|
|
213
|
+
hasProp(segmentLike, "removedClientIds", "array") &&
|
|
214
|
+
hasProp(segmentLike, "removedSeq", "number")
|
|
215
|
+
? segmentLike
|
|
216
|
+
: undefined;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* A type-guard which determines if the segment has removal info, and
|
|
220
|
+
* returns true if it does, along with applying strong typing.
|
|
221
|
+
*
|
|
222
|
+
* @param segmentLike - The segment-like object to check.
|
|
223
|
+
* @returns True if the segment has removal info, otherwise false.
|
|
224
|
+
*/
|
|
225
|
+
export const isRemoved = (segmentLike: unknown): segmentLike is IRemovalInfo =>
|
|
226
|
+
toRemovalInfo(segmentLike) !== undefined;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Asserts that the segment has removal info. Usage of this function should not produce a user facing error.
|
|
230
|
+
*
|
|
231
|
+
* @param segmentLike - The segment-like object to check.
|
|
232
|
+
* @throws Will throw an error if the segment does not have removal info.
|
|
233
|
+
*/
|
|
234
|
+
export const assertRemoved: <T extends Partial<IRemovalInfo> | undefined>(
|
|
235
|
+
segmentLike: ISegmentInternal | Partial<IRemovalInfo> | T,
|
|
236
|
+
) => asserts segmentLike is IRemovalInfo | Exclude<T, Partial<IRemovalInfo>> = (segmentLike) =>
|
|
237
|
+
assert(segmentLike === undefined || isRemoved(segmentLike), 0xaa2 /* must be removalInfo */);
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Removes the removal info. This is used in rollback.
|
|
241
|
+
* @param segmentLike - The segment-like object to check.
|
|
242
|
+
* @returns This function will change the type of the provided node like to never via an assertion. This
|
|
243
|
+
* ensures no further usage of the removed removal info is allowed. if continued use is required other
|
|
244
|
+
* type coercion methods should be use to correctly re-type the variable.
|
|
245
|
+
*/
|
|
246
|
+
export const removeRemovalInfo: (nodeLike: IRemovalInfo) => asserts nodeLike is never = (
|
|
247
|
+
nodeLike,
|
|
248
|
+
) =>
|
|
249
|
+
Object.assign<IRemovalInfo, Record<keyof IRemovalInfo, undefined>>(nodeLike, {
|
|
250
|
+
localRemovedSeq: undefined,
|
|
251
|
+
removedClientIds: undefined,
|
|
252
|
+
removedSeq: undefined,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Tracks information about when and where this segment was moved to.
|
|
257
|
+
*
|
|
258
|
+
* Note that merge-tree does not currently support moving and only supports
|
|
259
|
+
* obliterate. The fields below include "move" in their names to avoid renaming
|
|
260
|
+
* in the future, when moves _are_ supported.
|
|
261
|
+
* @legacy
|
|
262
|
+
* @alpha
|
|
263
|
+
* @deprecated - This interface will be removed in 2.20 with no replacement.
|
|
264
|
+
*/
|
|
265
|
+
export interface IMoveInfo {
|
|
266
|
+
/**
|
|
267
|
+
* Local seq at which this segment was moved if the move is yet-to-be
|
|
268
|
+
* acked.
|
|
269
|
+
*/
|
|
270
|
+
localMovedSeq?: number;
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* The first seq at which this segment was moved.
|
|
274
|
+
*/
|
|
275
|
+
movedSeq: number;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* All seqs at which this segment was moved. In the case of overlapping,
|
|
279
|
+
* concurrent moves this array will contain multiple seqs.
|
|
280
|
+
*
|
|
281
|
+
* The seq at `movedSeqs[i]` corresponds to the client id at `movedClientIds[i]`.
|
|
282
|
+
*
|
|
283
|
+
* The first element corresponds to the seq of the first move
|
|
284
|
+
*/
|
|
285
|
+
movedSeqs: number[];
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* A reference to the inserted destination segment corresponding to this
|
|
289
|
+
* segment's move.
|
|
290
|
+
*
|
|
291
|
+
* If undefined, the move was an obliterate.
|
|
292
|
+
*
|
|
293
|
+
* Currently this field is unused, as we only support obliterate operations
|
|
294
|
+
*/
|
|
295
|
+
moveDst?: ReferencePosition;
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* List of client IDs that have moved this segment.
|
|
299
|
+
*
|
|
300
|
+
* The client that actually moved the segment (i.e. whose move op was sequenced
|
|
301
|
+
* first) is stored as the first client in this list. Other clients in the
|
|
302
|
+
* list have all issued concurrent ops to move the segment.
|
|
303
|
+
*/
|
|
304
|
+
movedClientIds: number[];
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* If this segment was inserted into a concurrently moved range and
|
|
308
|
+
* the move op was sequenced before the insertion op. In this case,
|
|
309
|
+
* the segment is visible only to the inserting client
|
|
310
|
+
*
|
|
311
|
+
* `wasMovedOnInsert` only applies for acked obliterates. That is, if
|
|
312
|
+
* a segment inserted by a remote client is moved on insertion by a local
|
|
313
|
+
* and unacked obliterate, we do not consider it as having been moved
|
|
314
|
+
* on insert
|
|
315
|
+
*
|
|
316
|
+
* If a segment is moved on insertion, its length is only ever visible to
|
|
317
|
+
* the client that inserted the segment. This is relevant in partial length
|
|
318
|
+
* calculations
|
|
319
|
+
*/
|
|
320
|
+
wasMovedOnInsert: boolean;
|
|
321
|
+
}
|
|
322
|
+
export const toMoveInfo = (segmentLike: unknown): IMoveInfo | undefined =>
|
|
323
|
+
hasProp(segmentLike, "movedClientIds", "array") &&
|
|
324
|
+
hasProp(segmentLike, "movedSeq", "number") &&
|
|
325
|
+
hasProp(segmentLike, "movedSeqs", "array") &&
|
|
326
|
+
hasProp(segmentLike, "wasMovedOnInsert", "boolean")
|
|
327
|
+
? segmentLike
|
|
328
|
+
: undefined;
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* A type-guard which determines if the segment has move info, and
|
|
332
|
+
* returns true if it does, along with applying strong typing.
|
|
333
|
+
*
|
|
334
|
+
* @param segmentLike - The segment-like object to check.
|
|
335
|
+
* @returns True if the segment has move info, otherwise false.
|
|
336
|
+
*/
|
|
337
|
+
export const isMoved = (segmentLike: unknown): segmentLike is IMoveInfo =>
|
|
338
|
+
toMoveInfo(segmentLike) !== undefined;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Asserts that the segment has move info. Usage of this function should not produce a user facing error.
|
|
342
|
+
*
|
|
343
|
+
* @param segmentLike - The segment-like object to check.
|
|
344
|
+
* @throws Will throw an error if the segment does not have move info.
|
|
345
|
+
*/
|
|
346
|
+
export const assertMoved: <T extends Partial<IMoveInfo> | undefined>(
|
|
347
|
+
segmentLike: ISegmentInternal | Partial<IMoveInfo> | T,
|
|
348
|
+
) => asserts segmentLike is IMoveInfo | Exclude<T, Partial<IMoveInfo>> = (segmentLike) =>
|
|
349
|
+
assert(segmentLike === undefined || isMoved(segmentLike), 0xaa3 /* must be moveInfo */);
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* A union type representing any segment info.
|
|
353
|
+
*/
|
|
354
|
+
export type SegmentInfo = IMergeNodeInfo | IInsertionInfo | IMoveInfo | IRemovalInfo;
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* A type representing a segment with additional info.
|
|
358
|
+
*/
|
|
359
|
+
export type SegmentWithInfo<
|
|
360
|
+
T extends SegmentInfo,
|
|
361
|
+
S extends ISegmentPrivate = ISegmentPrivate,
|
|
362
|
+
> = S & T;
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Overwrites the segment info on a segment-like object.
|
|
366
|
+
*
|
|
367
|
+
* @param segmentLike - The segment-like object to set the info on.
|
|
368
|
+
* @param info - The segment info to overwrite.
|
|
369
|
+
* @returns The segment-like object with the info set.
|
|
370
|
+
*/
|
|
371
|
+
export const overwriteInfo = <
|
|
372
|
+
T extends SegmentInfo,
|
|
373
|
+
S extends ISegmentPrivate = ISegmentPrivate,
|
|
374
|
+
>(
|
|
375
|
+
segmentLike: S,
|
|
376
|
+
info: T,
|
|
377
|
+
): SegmentWithInfo<T, S> => Object.assign(segmentLike, info);
|
package/src/snapshotLoader.ts
CHANGED
|
@@ -20,12 +20,20 @@ import {
|
|
|
20
20
|
createChildLogger,
|
|
21
21
|
} from "@fluidframework/telemetry-utils/internal";
|
|
22
22
|
|
|
23
|
-
// eslint-disable-next-line import/no-deprecated
|
|
24
23
|
import { Client } from "./client.js";
|
|
25
24
|
import { NonCollabClient, UniversalSequenceNumber } from "./constants.js";
|
|
26
25
|
import { MergeTree } from "./mergeTree.js";
|
|
27
|
-
import {
|
|
26
|
+
import { ISegmentPrivate } from "./mergeTreeNodes.js";
|
|
28
27
|
import { IJSONSegment } from "./ops.js";
|
|
28
|
+
import {
|
|
29
|
+
// eslint-disable-next-line import/no-deprecated
|
|
30
|
+
IRemovalInfo,
|
|
31
|
+
overwriteInfo,
|
|
32
|
+
type IInsertionInfo,
|
|
33
|
+
// eslint-disable-next-line import/no-deprecated
|
|
34
|
+
type IMoveInfo,
|
|
35
|
+
type SegmentWithInfo,
|
|
36
|
+
} from "./segmentInfos.js";
|
|
29
37
|
import {
|
|
30
38
|
IJSONSegmentWithMergeInfo,
|
|
31
39
|
MergeTreeChunkV1,
|
|
@@ -39,7 +47,7 @@ export class SnapshotLoader {
|
|
|
39
47
|
|
|
40
48
|
constructor(
|
|
41
49
|
private readonly runtime: IFluidDataStoreRuntime,
|
|
42
|
-
|
|
50
|
+
|
|
43
51
|
private readonly client: Client,
|
|
44
52
|
private readonly mergeTree: MergeTree,
|
|
45
53
|
logger: ITelemetryLoggerExt,
|
|
@@ -96,60 +104,57 @@ export class SnapshotLoader {
|
|
|
96
104
|
|
|
97
105
|
private readonly specToSegment = (
|
|
98
106
|
spec: IJSONSegment | IJSONSegmentWithMergeInfo,
|
|
99
|
-
):
|
|
100
|
-
let seg: ISegmentLeaf;
|
|
101
|
-
|
|
107
|
+
): SegmentWithInfo<IInsertionInfo> => {
|
|
102
108
|
if (hasMergeInfo(spec)) {
|
|
103
|
-
seg = this.client.specToSegment(spec.json)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
spec.
|
|
109
|
-
|
|
110
|
-
: this.client.getOrAddShortClientId(spec.client);
|
|
111
|
-
|
|
112
|
-
seg.seq = spec.seq ?? UniversalSequenceNumber;
|
|
109
|
+
const seg = overwriteInfo<IInsertionInfo>(this.client.specToSegment(spec.json), {
|
|
110
|
+
clientId:
|
|
111
|
+
spec.client === undefined
|
|
112
|
+
? NonCollabClient
|
|
113
|
+
: this.client.getOrAddShortClientId(spec.client),
|
|
114
|
+
seq: spec.seq ?? UniversalSequenceNumber,
|
|
115
|
+
});
|
|
113
116
|
|
|
114
117
|
if (spec.removedSeq !== undefined) {
|
|
115
|
-
|
|
118
|
+
// this format had a bug where it didn't store all the overlap clients
|
|
119
|
+
// this is for back compat, so we change the singular id to an array
|
|
120
|
+
// this will only cause problems if there is an overlapping delete
|
|
121
|
+
// spanning the snapshot, which should be rare
|
|
122
|
+
const specAsBuggyFormat: IJSONSegmentWithMergeInfo & { removedClient?: string } = spec;
|
|
123
|
+
if (specAsBuggyFormat.removedClient !== undefined) {
|
|
124
|
+
spec.removedClientIds ??= [specAsBuggyFormat.removedClient];
|
|
125
|
+
}
|
|
126
|
+
assert(spec.removedClientIds !== undefined, 0xaa4 /* must have removedClient ids */);
|
|
127
|
+
// eslint-disable-next-line import/no-deprecated
|
|
128
|
+
overwriteInfo<IRemovalInfo>(seg, {
|
|
129
|
+
removedSeq: spec.removedSeq,
|
|
130
|
+
removedClientIds: spec.removedClientIds.map((id) =>
|
|
131
|
+
this.client.getOrAddShortClientId(id),
|
|
132
|
+
),
|
|
133
|
+
});
|
|
116
134
|
}
|
|
117
135
|
if (spec.movedSeq !== undefined) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
seg.movedSeqs = spec.movedSeqs;
|
|
122
|
-
}
|
|
123
|
-
// this format had a bug where it didn't store all the overlap clients
|
|
124
|
-
// this is for back compat, so we change the singular id to an array
|
|
125
|
-
// this will only cause problems if there is an overlapping delete
|
|
126
|
-
// spanning the snapshot, which should be rare
|
|
127
|
-
const specAsBuggyFormat: IJSONSegmentWithMergeInfo & { removedClient?: string } = spec;
|
|
128
|
-
if (specAsBuggyFormat.removedClient !== undefined) {
|
|
129
|
-
seg.removedClientIds = [
|
|
130
|
-
this.client.getOrAddShortClientId(specAsBuggyFormat.removedClient),
|
|
131
|
-
];
|
|
132
|
-
}
|
|
133
|
-
if (spec.removedClientIds !== undefined) {
|
|
134
|
-
seg.removedClientIds = spec.removedClientIds?.map((sid) =>
|
|
135
|
-
this.client.getOrAddShortClientId(sid),
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
if (spec.movedClientIds !== undefined) {
|
|
139
|
-
seg.movedClientIds = spec.movedClientIds?.map((sid) =>
|
|
140
|
-
this.client.getOrAddShortClientId(sid),
|
|
136
|
+
assert(
|
|
137
|
+
spec.movedClientIds !== undefined && spec.movedSeqs !== undefined,
|
|
138
|
+
0xaa5 /* must have movedIds ids */,
|
|
141
139
|
);
|
|
140
|
+
// eslint-disable-next-line import/no-deprecated
|
|
141
|
+
overwriteInfo<IMoveInfo>(seg, {
|
|
142
|
+
movedSeq: spec.movedSeq,
|
|
143
|
+
movedSeqs: spec.movedSeqs,
|
|
144
|
+
movedClientIds: spec.movedClientIds.map((id) =>
|
|
145
|
+
this.client.getOrAddShortClientId(id),
|
|
146
|
+
),
|
|
147
|
+
// BUG? This isn't persisted
|
|
148
|
+
wasMovedOnInsert: false,
|
|
149
|
+
});
|
|
142
150
|
}
|
|
143
|
-
} else {
|
|
144
|
-
seg = this.client.specToSegment(spec);
|
|
145
|
-
seg.seq = UniversalSequenceNumber;
|
|
146
151
|
|
|
147
|
-
|
|
148
|
-
// `NonCollabClient`.
|
|
149
|
-
seg.clientId = NonCollabClient;
|
|
152
|
+
return seg;
|
|
150
153
|
}
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
return overwriteInfo(this.client.specToSegment(spec), {
|
|
155
|
+
seq: UniversalSequenceNumber,
|
|
156
|
+
clientId: NonCollabClient,
|
|
157
|
+
});
|
|
153
158
|
};
|
|
154
159
|
|
|
155
160
|
private loadHeader(header: string): MergeTreeChunkV1 {
|
|
@@ -207,7 +212,7 @@ export class SnapshotLoader {
|
|
|
207
212
|
}
|
|
208
213
|
|
|
209
214
|
let chunksWithAttribution = chunk1.attribution === undefined ? 0 : 1;
|
|
210
|
-
const segs:
|
|
215
|
+
const segs: SegmentWithInfo<IInsertionInfo>[] = [];
|
|
211
216
|
let lengthSofar = chunk1.length;
|
|
212
217
|
for (
|
|
213
218
|
let chunkIndex = 1;
|
|
@@ -244,7 +249,7 @@ export class SnapshotLoader {
|
|
|
244
249
|
|
|
245
250
|
// Helper to insert segments at the end of the MergeTree.
|
|
246
251
|
const mergeTree = this.mergeTree;
|
|
247
|
-
const append = (segments:
|
|
252
|
+
const append = (segments: ISegmentPrivate[], cli: number, seq: number): void => {
|
|
248
253
|
mergeTree.insertSegments(
|
|
249
254
|
mergeTree.root.cachedLength ?? 0,
|
|
250
255
|
segments,
|
|
@@ -256,7 +261,7 @@ export class SnapshotLoader {
|
|
|
256
261
|
};
|
|
257
262
|
|
|
258
263
|
// Helpers to batch-insert segments that are below the min seq
|
|
259
|
-
const batch:
|
|
264
|
+
const batch: SegmentWithInfo<IInsertionInfo>[] = [];
|
|
260
265
|
const flushBatch = (): void => {
|
|
261
266
|
if (batch.length > 0) {
|
|
262
267
|
append(batch, NonCollabClient, UniversalSequenceNumber);
|
|
@@ -264,23 +269,21 @@ export class SnapshotLoader {
|
|
|
264
269
|
};
|
|
265
270
|
|
|
266
271
|
for (const seg of segs) {
|
|
267
|
-
const
|
|
268
|
-
const seq = seg.seq;
|
|
269
|
-
|
|
272
|
+
const { clientId, seq } = seg;
|
|
270
273
|
// If the segment can be batch inserted, add it to the 'batch' array. Otherwise, flush
|
|
271
274
|
// any batched segments and then insert the current segment individually.
|
|
272
|
-
if (
|
|
275
|
+
if (clientId === NonCollabClient && seq === UniversalSequenceNumber) {
|
|
273
276
|
batch.push(seg);
|
|
274
277
|
} else {
|
|
275
278
|
flushBatch();
|
|
276
|
-
append([seg],
|
|
279
|
+
append([seg], clientId, seq);
|
|
277
280
|
}
|
|
278
281
|
}
|
|
279
282
|
|
|
280
283
|
flushBatch();
|
|
281
284
|
}
|
|
282
285
|
|
|
283
|
-
private extractAttribution(segments:
|
|
286
|
+
private extractAttribution(segments: ISegmentPrivate[], chunk: MergeTreeChunkV1): void {
|
|
284
287
|
if (chunk.attribution) {
|
|
285
288
|
const { attributionPolicy } = this.mergeTree;
|
|
286
289
|
if (attributionPolicy === undefined) {
|
package/src/snapshotV1.ts
CHANGED
|
@@ -22,9 +22,10 @@ import { IAttributionCollection } from "./attributionCollection.js";
|
|
|
22
22
|
import { UnassignedSequenceNumber } from "./constants.js";
|
|
23
23
|
import { MergeTree } from "./mergeTree.js";
|
|
24
24
|
import { walkAllChildSegments } from "./mergeTreeNodeWalk.js";
|
|
25
|
-
import {
|
|
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, isMoved, isRemoved } from "./segmentInfos.js";
|
|
28
29
|
import {
|
|
29
30
|
IJSONSegmentWithMergeInfo,
|
|
30
31
|
JsonSegmentSpecs,
|
|
@@ -208,7 +209,7 @@ export class SnapshotV1 {
|
|
|
208
209
|
};
|
|
209
210
|
|
|
210
211
|
// Helper to serialize the given `segment` and add it to the snapshot (if a segment is provided).
|
|
211
|
-
const pushSeg = (segment?:
|
|
212
|
+
const pushSeg = (segment?: ISegmentPrivate): void => {
|
|
212
213
|
if (segment) {
|
|
213
214
|
if (segment.properties !== undefined && Object.keys(segment.properties).length === 0) {
|
|
214
215
|
segment.properties = undefined;
|
|
@@ -221,8 +222,9 @@ export class SnapshotV1 {
|
|
|
221
222
|
}
|
|
222
223
|
};
|
|
223
224
|
|
|
224
|
-
let prev:
|
|
225
|
-
const extractSegment = (segment:
|
|
225
|
+
let prev: ISegmentPrivate | undefined;
|
|
226
|
+
const extractSegment = (segment: ISegmentPrivate): boolean => {
|
|
227
|
+
assertInserted(segment);
|
|
226
228
|
// Elide segments that do not need to be included in the snapshot. A segment may be elided if
|
|
227
229
|
// either condition is true:
|
|
228
230
|
// a) The segment has not yet been ACKed. We do not need to snapshot unACKed segments because
|
|
@@ -231,10 +233,8 @@ export class SnapshotV1 {
|
|
|
231
233
|
// segment, and therefore we can discard it.
|
|
232
234
|
if (
|
|
233
235
|
segment.seq === UnassignedSequenceNumber ||
|
|
234
|
-
|
|
235
|
-
segment.
|
|
236
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
237
|
-
segment.movedSeq! <= minSeq
|
|
236
|
+
(isRemoved(segment) && segment.removedSeq <= minSeq) ||
|
|
237
|
+
(isMoved(segment) && segment.movedSeq <= minSeq)
|
|
238
238
|
) {
|
|
239
239
|
if (segment.seq !== UnassignedSequenceNumber) {
|
|
240
240
|
originalSegments += 1;
|
|
@@ -248,11 +248,10 @@ export class SnapshotV1 {
|
|
|
248
248
|
// (seq, client, etc.) This information is only needed if the segment is above the MSN (and doesn't
|
|
249
249
|
// have a pending remove.)
|
|
250
250
|
if (
|
|
251
|
-
//
|
|
252
|
-
segment
|
|
253
|
-
(segment.removedSeq === undefined || // .. Segment has not been removed, or...
|
|
251
|
+
segment.seq <= minSeq && // Segment is below the MSN, and...
|
|
252
|
+
(!isRemoved(segment) || // .. Segment has not been removed, or...
|
|
254
253
|
segment.removedSeq === UnassignedSequenceNumber) && // .. Removal op to be delivered on reconnect
|
|
255
|
-
(segment
|
|
254
|
+
(!isMoved(segment) || segment.movedSeq === UnassignedSequenceNumber)
|
|
256
255
|
) {
|
|
257
256
|
// This segment is below the MSN, which means that future ops will not reference it. Attempt to
|
|
258
257
|
// coalesce the new segment with the previous (if any).
|
|
@@ -286,14 +285,13 @@ export class SnapshotV1 {
|
|
|
286
285
|
json: segment.toJSONObject() as IJSONSegment,
|
|
287
286
|
};
|
|
288
287
|
// If the segment insertion is above the MSN, record the insertion merge info.
|
|
289
|
-
|
|
290
|
-
if (segment.seq! > minSeq) {
|
|
288
|
+
if (segment.seq > minSeq) {
|
|
291
289
|
raw.seq = segment.seq;
|
|
292
290
|
raw.client = this.getLongClientId(segment.clientId);
|
|
293
291
|
}
|
|
294
292
|
// We have already dispensed with removed segments below the MSN and removed segments with unassigned
|
|
295
293
|
// sequence numbers. Any remaining removal info should be preserved.
|
|
296
|
-
if (segment
|
|
294
|
+
if (isRemoved(segment)) {
|
|
297
295
|
assert(
|
|
298
296
|
segment.removedSeq !== UnassignedSequenceNumber && segment.removedSeq > minSeq,
|
|
299
297
|
0x065 /* "On removal info preservation, segment has invalid removed sequence number!" */,
|
|
@@ -311,7 +309,7 @@ export class SnapshotV1 {
|
|
|
311
309
|
);
|
|
312
310
|
}
|
|
313
311
|
|
|
314
|
-
if (segment
|
|
312
|
+
if (isMoved(segment)) {
|
|
315
313
|
assert(
|
|
316
314
|
segment.movedSeq !== UnassignedSequenceNumber && segment.movedSeq > minSeq,
|
|
317
315
|
0x873 /* On move info preservation, segment has invalid moved sequence number! */,
|