@fluidframework/merge-tree 2.31.0 → 2.32.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/client.d.ts +7 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +153 -44
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/mergeTree.d.ts +17 -5
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +188 -79
- package/dist/mergeTree.js.map +1 -1
- package/dist/mergeTreeNodes.d.ts +16 -18
- package/dist/mergeTreeNodes.d.ts.map +1 -1
- package/dist/mergeTreeNodes.js +6 -0
- package/dist/mergeTreeNodes.js.map +1 -1
- package/dist/perspective.d.ts +9 -0
- package/dist/perspective.d.ts.map +1 -1
- package/dist/perspective.js +14 -1
- package/dist/perspective.js.map +1 -1
- package/dist/segmentInfos.d.ts +32 -4
- package/dist/segmentInfos.d.ts.map +1 -1
- package/dist/segmentInfos.js +3 -1
- package/dist/segmentInfos.js.map +1 -1
- package/dist/sortedSegmentSet.d.ts +1 -0
- package/dist/sortedSegmentSet.d.ts.map +1 -1
- package/dist/sortedSegmentSet.js +3 -0
- package/dist/sortedSegmentSet.js.map +1 -1
- package/dist/test/beastTest.spec.js +5 -5
- package/dist/test/beastTest.spec.js.map +1 -1
- package/dist/test/client.localReference.spec.js +3 -3
- package/dist/test/client.localReference.spec.js.map +1 -1
- package/dist/test/client.rollback.spec.js +17 -0
- package/dist/test/client.rollback.spec.js.map +1 -1
- package/dist/test/clientTestHelper.d.ts +100 -0
- package/dist/test/clientTestHelper.d.ts.map +1 -0
- package/dist/test/clientTestHelper.js +196 -0
- package/dist/test/clientTestHelper.js.map +1 -0
- package/dist/test/mergeTree.annotate.spec.js +12 -12
- package/dist/test/mergeTree.annotate.spec.js.map +1 -1
- package/dist/test/mergeTree.markRangeRemoved.deltaCallback.spec.js +1 -1
- package/dist/test/mergeTree.markRangeRemoved.deltaCallback.spec.js.map +1 -1
- package/dist/test/obliterate.concurrent.spec.js +93 -90
- package/dist/test/obliterate.concurrent.spec.js.map +1 -1
- package/dist/test/obliterate.deltaCallback.spec.js +121 -116
- package/dist/test/obliterate.deltaCallback.spec.js.map +1 -1
- package/dist/test/obliterate.rangeExpansion.spec.js +29 -79
- package/dist/test/obliterate.rangeExpansion.spec.js.map +1 -1
- package/dist/test/obliterate.reconnect.spec.js +235 -58
- package/dist/test/obliterate.reconnect.spec.js.map +1 -1
- package/dist/test/testClient.js +1 -1
- package/dist/test/testClient.js.map +1 -1
- package/dist/test/testUtils.d.ts +13 -0
- package/dist/test/testUtils.d.ts.map +1 -1
- package/dist/test/testUtils.js +22 -1
- package/dist/test/testUtils.js.map +1 -1
- package/lib/client.d.ts +7 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +155 -46
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/mergeTree.d.ts +17 -5
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +192 -83
- package/lib/mergeTree.js.map +1 -1
- package/lib/mergeTreeNodes.d.ts +16 -18
- package/lib/mergeTreeNodes.d.ts.map +1 -1
- package/lib/mergeTreeNodes.js +7 -1
- package/lib/mergeTreeNodes.js.map +1 -1
- package/lib/perspective.d.ts +9 -0
- package/lib/perspective.d.ts.map +1 -1
- package/lib/perspective.js +12 -0
- package/lib/perspective.js.map +1 -1
- package/lib/segmentInfos.d.ts +32 -4
- package/lib/segmentInfos.d.ts.map +1 -1
- package/lib/segmentInfos.js +2 -1
- package/lib/segmentInfos.js.map +1 -1
- package/lib/sortedSegmentSet.d.ts +1 -0
- package/lib/sortedSegmentSet.d.ts.map +1 -1
- package/lib/sortedSegmentSet.js +3 -0
- package/lib/sortedSegmentSet.js.map +1 -1
- package/lib/test/beastTest.spec.js +5 -5
- package/lib/test/beastTest.spec.js.map +1 -1
- package/lib/test/client.localReference.spec.js +3 -3
- package/lib/test/client.localReference.spec.js.map +1 -1
- package/lib/test/client.rollback.spec.js +18 -1
- package/lib/test/client.rollback.spec.js.map +1 -1
- package/lib/test/clientTestHelper.d.ts +100 -0
- package/lib/test/clientTestHelper.d.ts.map +1 -0
- package/lib/test/clientTestHelper.js +192 -0
- package/lib/test/clientTestHelper.js.map +1 -0
- package/lib/test/mergeTree.annotate.spec.js +12 -12
- package/lib/test/mergeTree.annotate.spec.js.map +1 -1
- package/lib/test/mergeTree.markRangeRemoved.deltaCallback.spec.js +1 -1
- package/lib/test/mergeTree.markRangeRemoved.deltaCallback.spec.js.map +1 -1
- package/lib/test/obliterate.concurrent.spec.js +93 -90
- package/lib/test/obliterate.concurrent.spec.js.map +1 -1
- package/lib/test/obliterate.deltaCallback.spec.js +121 -116
- package/lib/test/obliterate.deltaCallback.spec.js.map +1 -1
- package/lib/test/obliterate.rangeExpansion.spec.js +1 -51
- package/lib/test/obliterate.rangeExpansion.spec.js.map +1 -1
- package/lib/test/obliterate.reconnect.spec.js +236 -59
- package/lib/test/obliterate.reconnect.spec.js.map +1 -1
- package/lib/test/testClient.js +1 -1
- package/lib/test/testClient.js.map +1 -1
- package/lib/test/testUtils.d.ts +13 -0
- package/lib/test/testUtils.d.ts.map +1 -1
- package/lib/test/testUtils.js +20 -0
- package/lib/test/testUtils.js.map +1 -1
- package/package.json +19 -18
- package/src/client.ts +286 -55
- package/src/index.ts +1 -1
- package/src/mergeTree.ts +265 -98
- package/src/mergeTreeNodes.ts +24 -18
- package/src/perspective.ts +21 -0
- package/src/segmentInfos.ts +48 -6
- package/src/sortedSegmentSet.ts +4 -0
- package/dist/test/partialSyncHelper.d.ts +0 -42
- package/dist/test/partialSyncHelper.d.ts.map +0 -1
- package/dist/test/partialSyncHelper.js +0 -96
- package/dist/test/partialSyncHelper.js.map +0 -1
- package/dist/test/reconnectHelper.d.ts +0 -50
- package/dist/test/reconnectHelper.d.ts.map +0 -1
- package/dist/test/reconnectHelper.js +0 -106
- package/dist/test/reconnectHelper.js.map +0 -1
- package/lib/test/partialSyncHelper.d.ts +0 -42
- package/lib/test/partialSyncHelper.d.ts.map +0 -1
- package/lib/test/partialSyncHelper.js +0 -92
- package/lib/test/partialSyncHelper.js.map +0 -1
- package/lib/test/reconnectHelper.d.ts +0 -50
- package/lib/test/reconnectHelper.d.ts.map +0 -1
- package/lib/test/reconnectHelper.js +0 -102
- package/lib/test/reconnectHelper.js.map +0 -1
package/src/mergeTreeNodes.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
UniversalSequenceNumber,
|
|
15
15
|
} from "./constants.js";
|
|
16
16
|
import { LocalReferenceCollection, type LocalReferencePosition } from "./localReference.js";
|
|
17
|
-
import { TrackingGroupCollection } from "./mergeTreeTracking.js";
|
|
17
|
+
import { TrackingGroupCollection, type ITrackingGroup } from "./mergeTreeTracking.js";
|
|
18
18
|
import { IJSONSegment, IMarkerDef, ReferenceType } from "./ops.js";
|
|
19
19
|
import { computeHierarchicalOrdinal } from "./ordinal.js";
|
|
20
20
|
import type { PartialSequenceLengths } from "./partialLengths.js";
|
|
@@ -32,8 +32,11 @@ import {
|
|
|
32
32
|
type IMergeNodeInfo,
|
|
33
33
|
type IHasRemovalInfo,
|
|
34
34
|
type SegmentWithInfo,
|
|
35
|
+
ISegmentInsideObliterateInfo,
|
|
36
|
+
isInsideObliterate,
|
|
35
37
|
} from "./segmentInfos.js";
|
|
36
38
|
import { PropertiesManager } from "./segmentPropertiesManager.js";
|
|
39
|
+
import type { Side } from "./sequencePlace.js";
|
|
37
40
|
import type { OperationStamp, SliceRemoveOperationStamp } from "./stamps.js";
|
|
38
41
|
|
|
39
42
|
/**
|
|
@@ -77,23 +80,6 @@ export interface ISegmentInternal extends ISegment {
|
|
|
77
80
|
export interface ISegmentPrivate extends ISegmentInternal {
|
|
78
81
|
segmentGroups?: SegmentGroupCollection;
|
|
79
82
|
propertyManager?: PropertiesManager;
|
|
80
|
-
/**
|
|
81
|
-
* Populated iff this segment was inserted into a range affected by concurrent obliterates at the time of its insertion.
|
|
82
|
-
* Contains information about the 'most recent' (i.e. 'winning' in the sense below) obliterate.
|
|
83
|
-
*
|
|
84
|
-
* BEWARE: We have opted for a certain form of last-write wins (LWW) semantics for obliterates:
|
|
85
|
-
* the client which last obliterated a range is considered to have "won ownership" of that range and may insert into it
|
|
86
|
-
* without that insertion being obliterated by other clients' concurrent obliterates.
|
|
87
|
-
*
|
|
88
|
-
* Therefore, this field can be populated even if the segment has not been obliterated (i.e. is still visible).
|
|
89
|
-
* This happens precisely when the segment was inserted by the same client that 'won' the obliterate (in a scenario where
|
|
90
|
-
* a client first issues a sided obliterate impacting a range, then inserts into that range before the server has acked the obliterate).
|
|
91
|
-
*
|
|
92
|
-
* See the test case "obliterate with mismatched final states" for an example of such a scenario.
|
|
93
|
-
*
|
|
94
|
-
* TODO:AB#29553: This property is not persisted in the summary, but it should be.
|
|
95
|
-
*/
|
|
96
|
-
obliteratePrecedingInsertion?: ObliterateInfo;
|
|
97
83
|
}
|
|
98
84
|
/**
|
|
99
85
|
* Segment leafs are segments that have both IMergeNodeInfo and IHasInsertionInfo. This means they
|
|
@@ -228,10 +214,24 @@ export interface InsertContext {
|
|
|
228
214
|
|
|
229
215
|
export interface ObliterateInfo {
|
|
230
216
|
start: LocalReferencePosition;
|
|
217
|
+
startSide: Side;
|
|
231
218
|
end: LocalReferencePosition;
|
|
219
|
+
endSide: Side;
|
|
232
220
|
refSeq: number;
|
|
233
221
|
stamp: SliceRemoveOperationStamp;
|
|
234
222
|
segmentGroup: SegmentGroup | undefined;
|
|
223
|
+
/**
|
|
224
|
+
* Defined only for unacked obliterates.
|
|
225
|
+
*
|
|
226
|
+
* Contains all segments inserted into the range this obliterate affects where at the time of insertion,
|
|
227
|
+
* this obliterate was the newest concurrent obliterate that overlapped the insertion point (this information
|
|
228
|
+
* is relevant for the tiebreak policy of allowing last-obliterater to insert).
|
|
229
|
+
*
|
|
230
|
+
* We need to keep this around for unacked ops because on reconnect, outstanding local obliterates may have set `obliteratePrecedingInsertion`
|
|
231
|
+
* (tiebreak) on segments they no longer apply to, since the reissued obliterate may affect a smaller range than the original one when content
|
|
232
|
+
* near the obliterate's endpoints was removed by another client between the time of the original obliterate and reissuing.
|
|
233
|
+
*/
|
|
234
|
+
tiebreakTrackingGroup: ITrackingGroup | undefined;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
export interface SegmentGroup {
|
|
@@ -423,6 +423,12 @@ export abstract class BaseSegment implements ISegment {
|
|
|
423
423
|
removes: [...this.removes],
|
|
424
424
|
});
|
|
425
425
|
}
|
|
426
|
+
if (isInsideObliterate(this)) {
|
|
427
|
+
overwriteInfo<ISegmentInsideObliterateInfo>(leafSegment, {
|
|
428
|
+
obliteratePrecedingInsertion: this.obliteratePrecedingInsertion,
|
|
429
|
+
insertionRefSeqStamp: this.insertionRefSeqStamp,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
426
432
|
|
|
427
433
|
this.trackingCollection.copyTo(leafSegment);
|
|
428
434
|
if (this.attribution) {
|
package/src/perspective.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { NonCollabClient } from "./constants.js";
|
|
6
7
|
import { seqLTE, type ISegment } from "./mergeTreeNodes.js";
|
|
7
8
|
import { isInserted, isRemoved } from "./segmentInfos.js";
|
|
8
9
|
import * as opstampUtils from "./stamps.js";
|
|
@@ -116,6 +117,21 @@ export class LocalReconnectingPerspective extends PerspectiveBase implements Per
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
/**
|
|
121
|
+
* A perspective which includes edits which were either:
|
|
122
|
+
* - acked and at or before some reference sequence number
|
|
123
|
+
* - unacked, but at or before some local sequence number
|
|
124
|
+
*
|
|
125
|
+
* @internal
|
|
126
|
+
*/
|
|
127
|
+
export function createLocalReconnectingPerspective(
|
|
128
|
+
refSeq: number,
|
|
129
|
+
clientId: number,
|
|
130
|
+
localSeq: number,
|
|
131
|
+
): Perspective {
|
|
132
|
+
return new LocalReconnectingPerspective(refSeq, clientId, localSeq);
|
|
133
|
+
}
|
|
134
|
+
|
|
119
135
|
/**
|
|
120
136
|
* A perspective which includes all known edits.
|
|
121
137
|
*
|
|
@@ -169,3 +185,8 @@ function isRemoveOperationStamp(stamp: OperationStamp): stamp is RemoveOperation
|
|
|
169
185
|
const { type } = stamp as unknown as RemoveOperationStamp;
|
|
170
186
|
return type === "setRemove" || type === "sliceRemove";
|
|
171
187
|
}
|
|
188
|
+
|
|
189
|
+
export const allAckedChangesPerspective = new PriorPerspective(
|
|
190
|
+
Number.MAX_SAFE_INTEGER,
|
|
191
|
+
NonCollabClient,
|
|
192
|
+
);
|
package/src/segmentInfos.ts
CHANGED
|
@@ -6,8 +6,13 @@
|
|
|
6
6
|
import { assert, isObject } from "@fluidframework/core-utils/internal";
|
|
7
7
|
|
|
8
8
|
import { UnassignedSequenceNumber } from "./constants.js";
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import {
|
|
10
|
+
ISegmentInternal,
|
|
11
|
+
ISegmentPrivate,
|
|
12
|
+
MergeBlock,
|
|
13
|
+
type ObliterateInfo,
|
|
14
|
+
} from "./mergeTreeNodes.js";
|
|
15
|
+
import type { InsertOperationStamp, OperationStamp, RemoveOperationStamp } from "./stamps.js";
|
|
11
16
|
|
|
12
17
|
export interface StringToType {
|
|
13
18
|
"string": string;
|
|
@@ -50,6 +55,34 @@ export interface IHasInsertionInfo {
|
|
|
50
55
|
insert: InsertOperationStamp;
|
|
51
56
|
}
|
|
52
57
|
|
|
58
|
+
export interface ISegmentInsideObliterateInfo {
|
|
59
|
+
/**
|
|
60
|
+
* Populated iff this segment was inserted into a range affected by concurrent obliterates at the time of its insertion.
|
|
61
|
+
* Contains information about the 'most recent' (i.e. 'winning' in the sense below) obliterate.
|
|
62
|
+
*
|
|
63
|
+
* BEWARE: We have opted for a certain form of last-write wins (LWW) semantics for obliterates:
|
|
64
|
+
* the client which last obliterated a range is considered to have "won ownership" of that range and may insert into it
|
|
65
|
+
* without that insertion being obliterated by other clients' concurrent obliterates.
|
|
66
|
+
*
|
|
67
|
+
* Therefore, this field can be populated even if the segment has not been obliterated (i.e. is still visible).
|
|
68
|
+
* This happens precisely when the segment was inserted by the same client that 'won' the obliterate (in a scenario where
|
|
69
|
+
* a client first issues a sided obliterate impacting a range, then inserts into that range before the server has acked the obliterate).
|
|
70
|
+
*
|
|
71
|
+
* See the test case "obliterate with mismatched final states" for an example of such a scenario.
|
|
72
|
+
*
|
|
73
|
+
* TODO:AB#29553: This property is not persisted in the V1 summary, but it should be.
|
|
74
|
+
*/
|
|
75
|
+
obliteratePrecedingInsertion?: ObliterateInfo;
|
|
76
|
+
/**
|
|
77
|
+
* Populated iff this segment was inserted into a range concurrently removed by a local obliterate operation.
|
|
78
|
+
* This field is unset once the newest such overlapping obliterate is acked, and allows recomputing {@link obliteratePrecedingInsertion}
|
|
79
|
+
* if that local obliterate is resubmitted.
|
|
80
|
+
*
|
|
81
|
+
* TODO:AB#29553: This property is not persisted in the V1 summary, but it should be.
|
|
82
|
+
*/
|
|
83
|
+
insertionRefSeqStamp?: OperationStamp;
|
|
84
|
+
}
|
|
85
|
+
|
|
53
86
|
/**
|
|
54
87
|
* Converts a segment-like object to an insertion info object if possible.
|
|
55
88
|
*
|
|
@@ -75,6 +108,11 @@ export const toInsertionInfo = (segmentLike: unknown): IHasInsertionInfo | undef
|
|
|
75
108
|
export const isInserted = (segmentLike: unknown): segmentLike is IHasInsertionInfo =>
|
|
76
109
|
toInsertionInfo(segmentLike) !== undefined;
|
|
77
110
|
|
|
111
|
+
export const isInsideObliterate = (
|
|
112
|
+
segmentLike: unknown,
|
|
113
|
+
): segmentLike is ISegmentInsideObliterateInfo =>
|
|
114
|
+
segmentLike !== undefined && hasProp(segmentLike, "obliteratePrecedingInsertion", "object");
|
|
115
|
+
|
|
78
116
|
/**
|
|
79
117
|
* Asserts that the segment has insertion info. Usage of this function should not produce a user facing error.
|
|
80
118
|
*
|
|
@@ -227,9 +265,9 @@ export const assertRemoved: <T extends Partial<IHasRemovalInfo> | undefined>(
|
|
|
227
265
|
* ensures no further usage of the removed removal info is allowed. if continued use is required other
|
|
228
266
|
* type coercion methods should be use to correctly re-type the variable.
|
|
229
267
|
*/
|
|
230
|
-
export const removeRemovalInfo: (
|
|
231
|
-
nodeLike,
|
|
232
|
-
) =>
|
|
268
|
+
export const removeRemovalInfo: (
|
|
269
|
+
nodeLike: IHasRemovalInfo,
|
|
270
|
+
) => asserts nodeLike is Record<keyof IHasRemovalInfo, never> = (nodeLike) =>
|
|
233
271
|
Object.assign<IHasRemovalInfo, Record<keyof IHasRemovalInfo, undefined>>(nodeLike, {
|
|
234
272
|
removes: undefined,
|
|
235
273
|
});
|
|
@@ -257,7 +295,11 @@ export function wasRemovedOnInsert(segment: IHasInsertionInfo & ISegmentPrivate)
|
|
|
257
295
|
/**
|
|
258
296
|
* A union type representing any segment info.
|
|
259
297
|
*/
|
|
260
|
-
export type SegmentInfo =
|
|
298
|
+
export type SegmentInfo =
|
|
299
|
+
| IMergeNodeInfo
|
|
300
|
+
| IHasInsertionInfo
|
|
301
|
+
| IHasRemovalInfo
|
|
302
|
+
| ISegmentInsideObliterateInfo;
|
|
261
303
|
|
|
262
304
|
/**
|
|
263
305
|
* A type representing a segment with additional info.
|
package/src/sortedSegmentSet.ts
CHANGED
|
@@ -57,6 +57,10 @@ export class SortedSegmentSet<
|
|
|
57
57
|
return 0;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
public onSortOrderChange(): void {
|
|
61
|
+
this.sortedItems.sort((a, b) => this.compare(a, b));
|
|
62
|
+
}
|
|
63
|
+
|
|
60
64
|
protected compare(a: T, b: T): number {
|
|
61
65
|
const aOrdinal = this.getOrdinal(a);
|
|
62
66
|
const bOrdinal = this.getOrdinal(b);
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
|
-
import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
6
|
-
import type { IMergeTreeOptions, InteriorSequencePlace } from "../index.js";
|
|
7
|
-
import type { TestClient } from "./testClient.js";
|
|
8
|
-
import { TestClientLogger } from "./testClientLogger.js";
|
|
9
|
-
declare const ClientIds: readonly ["A", "B", "C", "D"];
|
|
10
|
-
type ClientName = (typeof ClientIds)[number];
|
|
11
|
-
/**
|
|
12
|
-
* Like `ReconnectHelper`, but:
|
|
13
|
-
* - does not support reconnecting clients
|
|
14
|
-
* - supports advancing only some clients to a given sequence number (not all clients must be synchronized at the same time).
|
|
15
|
-
*
|
|
16
|
-
* This allows testing sequences of operations where clients have varying refSeqs, rather than having all clients advance refSeq
|
|
17
|
-
* in lockstep.
|
|
18
|
-
*/
|
|
19
|
-
export declare class PartialSyncTestHelper {
|
|
20
|
-
clients: Record<ClientName, TestClient> & {
|
|
21
|
-
all: TestClient[];
|
|
22
|
-
};
|
|
23
|
-
idxFromName(name: ClientName): number;
|
|
24
|
-
logger: TestClientLogger;
|
|
25
|
-
ops: ISequencedDocumentMessage[];
|
|
26
|
-
clientToLastAppliedSeq: Map<"A" | "C" | "B" | "D", number>;
|
|
27
|
-
perClientOps: ISequencedDocumentMessage[][];
|
|
28
|
-
private seq;
|
|
29
|
-
constructor(options?: IMergeTreeOptions);
|
|
30
|
-
private addMessage;
|
|
31
|
-
insertText(clientName: ClientName, pos: number, text: string): void;
|
|
32
|
-
removeRange(clientName: ClientName, start: number, end: number): void;
|
|
33
|
-
obliterateRange(clientName: ClientName, start: number | InteriorSequencePlace, end: number | InteriorSequencePlace): void;
|
|
34
|
-
advanceClientToSeq(clientName: ClientName, seq: number): void;
|
|
35
|
-
/**
|
|
36
|
-
* Sends all known ops to the procieded client ids.
|
|
37
|
-
*/
|
|
38
|
-
advanceClients(...clientNames: ClientName[]): void;
|
|
39
|
-
processAllOps(): void;
|
|
40
|
-
}
|
|
41
|
-
export {};
|
|
42
|
-
//# sourceMappingURL=partialSyncHelper.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"partialSyncHelper.d.ts","sourceRoot":"","sources":["../../src/test/partialSyncHelper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAExF,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAA+B,MAAM,uBAAuB,CAAC;AAEtF,QAAA,MAAM,SAAS,+BAAgC,CAAC;AAChD,KAAK,UAAU,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7C;;;;;;;GAOG;AACH,qBAAa,qBAAqB;IACjC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG;QAAE,GAAG,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC;IAEhE,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM;IAIrC,MAAM,EAAE,gBAAgB,CAAC;IAEzB,GAAG,EAAE,yBAAyB,EAAE,CAAM;IACtC,sBAAsB,qCAAiC;IAEvD,YAAY,EAAE,yBAAyB,EAAE,EAAE,CAAC;IAE5C,OAAO,CAAC,GAAG,CAAa;gBAEL,OAAO,GAAE,iBAAsB;IAgBlD,OAAO,CAAC,UAAU;IAUX,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKnE,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAKrE,eAAe,CACrB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,MAAM,GAAG,qBAAqB,EACrC,GAAG,EAAE,MAAM,GAAG,qBAAqB,GACjC,IAAI;IAWA,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAwBpE;;OAEG;IACI,cAAc,CAAC,GAAG,WAAW,EAAE,UAAU,EAAE,GAAG,IAAI;IAOlD,aAAa,IAAI,IAAI;CAM5B"}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*!
|
|
3
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
4
|
-
* Licensed under the MIT License.
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.PartialSyncTestHelper = void 0;
|
|
8
|
-
const node_assert_1 = require("node:assert");
|
|
9
|
-
const testClientLogger_js_1 = require("./testClientLogger.js");
|
|
10
|
-
const ClientIds = ["A", "B", "C", "D"];
|
|
11
|
-
/**
|
|
12
|
-
* Like `ReconnectHelper`, but:
|
|
13
|
-
* - does not support reconnecting clients
|
|
14
|
-
* - supports advancing only some clients to a given sequence number (not all clients must be synchronized at the same time).
|
|
15
|
-
*
|
|
16
|
-
* This allows testing sequences of operations where clients have varying refSeqs, rather than having all clients advance refSeq
|
|
17
|
-
* in lockstep.
|
|
18
|
-
*/
|
|
19
|
-
class PartialSyncTestHelper {
|
|
20
|
-
idxFromName(name) {
|
|
21
|
-
return (name.codePointAt(0) ?? 0) - ("A".codePointAt(0) ?? 0);
|
|
22
|
-
}
|
|
23
|
-
constructor(options = {}) {
|
|
24
|
-
this.ops = [];
|
|
25
|
-
this.clientToLastAppliedSeq = new Map();
|
|
26
|
-
this.seq = 0;
|
|
27
|
-
this.clients = (0, testClientLogger_js_1.createClientsAtInitialState)({
|
|
28
|
-
initialState: "",
|
|
29
|
-
options: {
|
|
30
|
-
mergeTreeEnableObliterate: true,
|
|
31
|
-
mergeTreeEnableSidedObliterate: true,
|
|
32
|
-
...options,
|
|
33
|
-
},
|
|
34
|
-
}, ...ClientIds);
|
|
35
|
-
this.logger = new testClientLogger_js_1.TestClientLogger(this.clients.all);
|
|
36
|
-
this.perClientOps = this.clients.all.map(() => []);
|
|
37
|
-
}
|
|
38
|
-
addMessage(message) {
|
|
39
|
-
this.ops.push(message);
|
|
40
|
-
// This implementation (specifically, that of applying ops / synchronizing clients) assumes messages
|
|
41
|
-
// are pushed sequentially starting with seq 1.
|
|
42
|
-
(0, node_assert_1.strict)(message.sequenceNumber === this.ops.length, "Partial sync test helper invariant violated");
|
|
43
|
-
}
|
|
44
|
-
insertText(clientName, pos, text) {
|
|
45
|
-
const client = this.clients[clientName];
|
|
46
|
-
this.addMessage(client.makeOpMessage(client.insertTextLocal(pos, text), ++this.seq));
|
|
47
|
-
}
|
|
48
|
-
removeRange(clientName, start, end) {
|
|
49
|
-
const client = this.clients[clientName];
|
|
50
|
-
this.addMessage(client.makeOpMessage(client.removeRangeLocal(start, end), ++this.seq));
|
|
51
|
-
}
|
|
52
|
-
obliterateRange(clientName, start, end) {
|
|
53
|
-
const client = this.clients[clientName];
|
|
54
|
-
this.addMessage(client.makeOpMessage(
|
|
55
|
-
// TODO: remove type assertions when sidedness is enabled
|
|
56
|
-
client.obliterateRangeLocal(start, end), ++this.seq));
|
|
57
|
-
}
|
|
58
|
-
advanceClientToSeq(clientName, seq) {
|
|
59
|
-
const client = this.clients[clientName];
|
|
60
|
-
const lastApplied = this.clientToLastAppliedSeq.get(clientName);
|
|
61
|
-
(0, node_assert_1.strict)(seq > 0, "Can only advance clients to sequence numbers that exist");
|
|
62
|
-
(0, node_assert_1.strict)(this.ops.length >= seq, "Cannot attempt to advance clients to sequence numbers that don't yet exist");
|
|
63
|
-
let startIndex;
|
|
64
|
-
if (lastApplied === undefined) {
|
|
65
|
-
startIndex = 0;
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
if (lastApplied >= seq) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
startIndex = lastApplied;
|
|
72
|
-
}
|
|
73
|
-
for (let i = startIndex; i < seq; i++) {
|
|
74
|
-
const nextMessage = this.ops[i];
|
|
75
|
-
client.applyMsg(nextMessage);
|
|
76
|
-
this.clientToLastAppliedSeq.set(clientName, nextMessage.sequenceNumber);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Sends all known ops to the procieded client ids.
|
|
81
|
-
*/
|
|
82
|
-
advanceClients(...clientNames) {
|
|
83
|
-
const latestSeq = this.ops[this.ops.length - 1].sequenceNumber;
|
|
84
|
-
for (const name of clientNames) {
|
|
85
|
-
this.advanceClientToSeq(name, latestSeq);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
processAllOps() {
|
|
89
|
-
const latestSeq = this.ops[this.ops.length - 1].sequenceNumber;
|
|
90
|
-
for (const name of ClientIds) {
|
|
91
|
-
this.advanceClientToSeq(name, latestSeq);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
exports.PartialSyncTestHelper = PartialSyncTestHelper;
|
|
96
|
-
//# sourceMappingURL=partialSyncHelper.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"partialSyncHelper.js","sourceRoot":"","sources":["../../src/test/partialSyncHelper.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\n\nimport type { IMergeTreeOptions, InteriorSequencePlace } from \"../index.js\";\n\nimport type { TestClient } from \"./testClient.js\";\nimport { TestClientLogger, createClientsAtInitialState } from \"./testClientLogger.js\";\n\nconst ClientIds = [\"A\", \"B\", \"C\", \"D\"] as const;\ntype ClientName = (typeof ClientIds)[number];\n\n/**\n * Like `ReconnectHelper`, but:\n * - does not support reconnecting clients\n * - supports advancing only some clients to a given sequence number (not all clients must be synchronized at the same time).\n *\n * This allows testing sequences of operations where clients have varying refSeqs, rather than having all clients advance refSeq\n * in lockstep.\n */\nexport class PartialSyncTestHelper {\n\tclients: Record<ClientName, TestClient> & { all: TestClient[] };\n\n\tidxFromName(name: ClientName): number {\n\t\treturn (name.codePointAt(0) ?? 0) - (\"A\".codePointAt(0) ?? 0);\n\t}\n\n\tlogger: TestClientLogger;\n\n\tops: ISequencedDocumentMessage[] = [];\n\tclientToLastAppliedSeq = new Map<ClientName, number>();\n\n\tperClientOps: ISequencedDocumentMessage[][];\n\n\tprivate seq: number = 0;\n\n\tpublic constructor(options: IMergeTreeOptions = {}) {\n\t\tthis.clients = createClientsAtInitialState(\n\t\t\t{\n\t\t\t\tinitialState: \"\",\n\t\t\t\toptions: {\n\t\t\t\t\tmergeTreeEnableObliterate: true,\n\t\t\t\t\tmergeTreeEnableSidedObliterate: true,\n\t\t\t\t\t...options,\n\t\t\t\t},\n\t\t\t},\n\t\t\t...ClientIds,\n\t\t);\n\t\tthis.logger = new TestClientLogger(this.clients.all);\n\t\tthis.perClientOps = this.clients.all.map(() => []);\n\t}\n\n\tprivate addMessage(message: ISequencedDocumentMessage): void {\n\t\tthis.ops.push(message);\n\t\t// This implementation (specifically, that of applying ops / synchronizing clients) assumes messages\n\t\t// are pushed sequentially starting with seq 1.\n\t\tassert(\n\t\t\tmessage.sequenceNumber === this.ops.length,\n\t\t\t\"Partial sync test helper invariant violated\",\n\t\t);\n\t}\n\n\tpublic insertText(clientName: ClientName, pos: number, text: string): void {\n\t\tconst client = this.clients[clientName];\n\t\tthis.addMessage(client.makeOpMessage(client.insertTextLocal(pos, text), ++this.seq));\n\t}\n\n\tpublic removeRange(clientName: ClientName, start: number, end: number): void {\n\t\tconst client = this.clients[clientName];\n\t\tthis.addMessage(client.makeOpMessage(client.removeRangeLocal(start, end), ++this.seq));\n\t}\n\n\tpublic obliterateRange(\n\t\tclientName: ClientName,\n\t\tstart: number | InteriorSequencePlace,\n\t\tend: number | InteriorSequencePlace,\n\t): void {\n\t\tconst client = this.clients[clientName];\n\t\tthis.addMessage(\n\t\t\tclient.makeOpMessage(\n\t\t\t\t// TODO: remove type assertions when sidedness is enabled\n\t\t\t\tclient.obliterateRangeLocal(start as number, end as number),\n\t\t\t\t++this.seq,\n\t\t\t),\n\t\t);\n\t}\n\n\tpublic advanceClientToSeq(clientName: ClientName, seq: number): void {\n\t\tconst client = this.clients[clientName];\n\t\tconst lastApplied = this.clientToLastAppliedSeq.get(clientName);\n\t\tassert(seq > 0, \"Can only advance clients to sequence numbers that exist\");\n\t\tassert(\n\t\t\tthis.ops.length >= seq,\n\t\t\t\"Cannot attempt to advance clients to sequence numbers that don't yet exist\",\n\t\t);\n\t\tlet startIndex: number;\n\t\tif (lastApplied === undefined) {\n\t\t\tstartIndex = 0;\n\t\t} else {\n\t\t\tif (lastApplied >= seq) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstartIndex = lastApplied;\n\t\t}\n\t\tfor (let i = startIndex; i < seq; i++) {\n\t\t\tconst nextMessage = this.ops[i];\n\t\t\tclient.applyMsg(nextMessage);\n\t\t\tthis.clientToLastAppliedSeq.set(clientName, nextMessage.sequenceNumber);\n\t\t}\n\t}\n\n\t/**\n\t * Sends all known ops to the procieded client ids.\n\t */\n\tpublic advanceClients(...clientNames: ClientName[]): void {\n\t\tconst latestSeq = this.ops[this.ops.length - 1].sequenceNumber;\n\t\tfor (const name of clientNames) {\n\t\t\tthis.advanceClientToSeq(name, latestSeq);\n\t\t}\n\t}\n\n\tpublic processAllOps(): void {\n\t\tconst latestSeq = this.ops[this.ops.length - 1].sequenceNumber;\n\t\tfor (const name of ClientIds) {\n\t\t\tthis.advanceClientToSeq(name, latestSeq);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
|
-
import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
6
|
-
import { type IMergeTreeOptions, type InteriorSequencePlace, type SequencePlace } from "../index.js";
|
|
7
|
-
import type { SegmentGroup } from "../mergeTreeNodes.js";
|
|
8
|
-
import { IMergeTreeDeltaOp, type IMergeTreeInsertMsg, type IMergeTreeObliterateMsg, type IMergeTreeObliterateSidedMsg, type IMergeTreeRemoveMsg } from "../ops.js";
|
|
9
|
-
import type { TestClient } from "./testClient.js";
|
|
10
|
-
import { TestClientLogger } from "./testClientLogger.js";
|
|
11
|
-
declare const ClientIds: readonly ["A", "B", "C", "D"];
|
|
12
|
-
type ClientName = (typeof ClientIds)[number];
|
|
13
|
-
export declare class ReconnectTestHelper {
|
|
14
|
-
clients: Record<ClientName, TestClient> & {
|
|
15
|
-
all: TestClient[];
|
|
16
|
-
};
|
|
17
|
-
idxFromName(name: ClientName): number;
|
|
18
|
-
logger: TestClientLogger;
|
|
19
|
-
ops: ISequencedDocumentMessage[];
|
|
20
|
-
perClientOps: ISequencedDocumentMessage[][];
|
|
21
|
-
seq: number;
|
|
22
|
-
constructor(options?: IMergeTreeOptions);
|
|
23
|
-
insertText(clientName: ClientName, pos: number, text: string): void;
|
|
24
|
-
removeRange(clientName: ClientName, start: number, end: number): void;
|
|
25
|
-
obliterateRange(clientName: ClientName, start: number | InteriorSequencePlace, end: number | InteriorSequencePlace): void;
|
|
26
|
-
insertTextLocal(clientName: ClientName, pos: number, text: string): {
|
|
27
|
-
op: IMergeTreeInsertMsg;
|
|
28
|
-
seg: SegmentGroup;
|
|
29
|
-
refSeq: number;
|
|
30
|
-
};
|
|
31
|
-
removeRangeLocal(clientName: ClientName, start: number, end: number): {
|
|
32
|
-
op: IMergeTreeRemoveMsg;
|
|
33
|
-
seg: SegmentGroup;
|
|
34
|
-
refSeq: number;
|
|
35
|
-
};
|
|
36
|
-
obliterateRangeLocal(clientName: ClientName, start: SequencePlace, end: SequencePlace): {
|
|
37
|
-
op: IMergeTreeObliterateMsg | IMergeTreeObliterateSidedMsg;
|
|
38
|
-
seg: SegmentGroup;
|
|
39
|
-
refSeq: number;
|
|
40
|
-
};
|
|
41
|
-
disconnect(clientNames: ClientName[]): void;
|
|
42
|
-
processAllOps(): void;
|
|
43
|
-
reconnect(clientNames: ClientName[]): void;
|
|
44
|
-
submitDisconnectedOp(clientName: ClientName, op: {
|
|
45
|
-
op: IMergeTreeDeltaOp;
|
|
46
|
-
seg: SegmentGroup | SegmentGroup[];
|
|
47
|
-
}): void;
|
|
48
|
-
}
|
|
49
|
-
export {};
|
|
50
|
-
//# sourceMappingURL=reconnectHelper.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"reconnectHelper.d.ts","sourceRoot":"","sources":["../../src/test/reconnectHelper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAExF,OAAO,EAEN,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EACN,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,mBAAmB,EACxB,MAAM,WAAW,CAAC;AAEnB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAA+B,MAAM,uBAAuB,CAAC;AAEtF,QAAA,MAAM,SAAS,+BAAgC,CAAC;AAChD,KAAK,UAAU,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7C,qBAAa,mBAAmB;IAC/B,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG;QAAE,GAAG,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC;IAEhE,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM;IAIrC,MAAM,EAAE,gBAAgB,CAAC;IAEzB,GAAG,EAAE,yBAAyB,EAAE,CAAM;IACtC,YAAY,EAAE,yBAAyB,EAAE,EAAE,CAAC;IAE5C,GAAG,EAAE,MAAM,CAAK;gBAEG,OAAO,GAAE,iBAAsB;IAgB3C,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKnE,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAKrE,eAAe,CACrB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,MAAM,GAAG,qBAAqB,EACrC,GAAG,EAAE,MAAM,GAAG,qBAAqB,GACjC,IAAI;IAWA,eAAe,CACrB,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GACV;QACF,EAAE,EAAE,mBAAmB,CAAC;QACxB,GAAG,EAAE,YAAY,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KACf;IASM,gBAAgB,CACtB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACT;QACF,EAAE,EAAE,mBAAmB,CAAC;QACxB,GAAG,EAAE,YAAY,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KACf;IASM,oBAAoB,CAC1B,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,aAAa,GAChB;QACF,EAAE,EAAE,uBAAuB,GAAG,4BAA4B,CAAC;QAC3D,GAAG,EAAE,YAAY,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KACf;IAoBM,UAAU,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,IAAI;IAY3C,aAAa,IAAI,IAAI;IAOrB,SAAS,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,IAAI;IAS1C,oBAAoB,CAC1B,UAAU,EAAE,UAAU,EACtB,EAAE,EAAE;QAAE,EAAE,EAAE,iBAAiB,CAAC;QAAC,GAAG,EAAE,YAAY,GAAG,YAAY,EAAE,CAAA;KAAE,GAC/D,IAAI;CAIP"}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*!
|
|
3
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
4
|
-
* Licensed under the MIT License.
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.ReconnectTestHelper = void 0;
|
|
8
|
-
const node_assert_1 = require("node:assert");
|
|
9
|
-
const index_js_1 = require("../index.js");
|
|
10
|
-
const testClientLogger_js_1 = require("./testClientLogger.js");
|
|
11
|
-
const ClientIds = ["A", "B", "C", "D"];
|
|
12
|
-
class ReconnectTestHelper {
|
|
13
|
-
idxFromName(name) {
|
|
14
|
-
return (name.codePointAt(0) ?? 0) - ("A".codePointAt(0) ?? 0);
|
|
15
|
-
}
|
|
16
|
-
constructor(options = {}) {
|
|
17
|
-
this.ops = [];
|
|
18
|
-
this.seq = 0;
|
|
19
|
-
this.clients = (0, testClientLogger_js_1.createClientsAtInitialState)({
|
|
20
|
-
initialState: "",
|
|
21
|
-
options: {
|
|
22
|
-
mergeTreeEnableObliterate: true,
|
|
23
|
-
mergeTreeEnableObliterateReconnect: true,
|
|
24
|
-
...options,
|
|
25
|
-
},
|
|
26
|
-
}, ...ClientIds);
|
|
27
|
-
this.logger = new testClientLogger_js_1.TestClientLogger(this.clients.all);
|
|
28
|
-
this.perClientOps = this.clients.all.map(() => []);
|
|
29
|
-
}
|
|
30
|
-
insertText(clientName, pos, text) {
|
|
31
|
-
const client = this.clients[clientName];
|
|
32
|
-
this.ops.push(client.makeOpMessage(client.insertTextLocal(pos, text), ++this.seq));
|
|
33
|
-
}
|
|
34
|
-
removeRange(clientName, start, end) {
|
|
35
|
-
const client = this.clients[clientName];
|
|
36
|
-
this.ops.push(client.makeOpMessage(client.removeRangeLocal(start, end), ++this.seq));
|
|
37
|
-
}
|
|
38
|
-
obliterateRange(clientName, start, end) {
|
|
39
|
-
const client = this.clients[clientName];
|
|
40
|
-
this.ops.push(client.makeOpMessage(
|
|
41
|
-
// TODO: remove type assertions when sidedness is enabled
|
|
42
|
-
client.obliterateRangeLocal(start, end), ++this.seq));
|
|
43
|
-
}
|
|
44
|
-
insertTextLocal(clientName, pos, text) {
|
|
45
|
-
const client = this.clients[clientName];
|
|
46
|
-
const op = client.insertTextLocal(pos, text);
|
|
47
|
-
(0, node_assert_1.strict)(op);
|
|
48
|
-
const seg = client.peekPendingSegmentGroups();
|
|
49
|
-
(0, node_assert_1.strict)(seg);
|
|
50
|
-
return { op, seg, refSeq: client.getCollabWindow().currentSeq };
|
|
51
|
-
}
|
|
52
|
-
removeRangeLocal(clientName, start, end) {
|
|
53
|
-
const client = this.clients[clientName];
|
|
54
|
-
const op = client.removeRangeLocal(start, end);
|
|
55
|
-
(0, node_assert_1.strict)(op);
|
|
56
|
-
const seg = client.peekPendingSegmentGroups();
|
|
57
|
-
(0, node_assert_1.strict)(seg);
|
|
58
|
-
return { op, seg, refSeq: client.getCollabWindow().currentSeq };
|
|
59
|
-
}
|
|
60
|
-
obliterateRangeLocal(clientName, start, end) {
|
|
61
|
-
const client = this.clients[clientName];
|
|
62
|
-
let { startPos, endPos } = (0, index_js_1.endpointPosAndSide)(start, end);
|
|
63
|
-
(0, node_assert_1.strict)(startPos !== undefined && endPos !== undefined, "start and end positions must be defined");
|
|
64
|
-
startPos = startPos === "start" ? 0 : startPos;
|
|
65
|
-
endPos = endPos === "end" ? client.getLength() : endPos;
|
|
66
|
-
(0, node_assert_1.strict)(startPos !== "end" && endPos !== "start", "start cannot be end and end cannot be start");
|
|
67
|
-
const op = client.obliterateRangeLocal(startPos, endPos);
|
|
68
|
-
(0, node_assert_1.strict)(op);
|
|
69
|
-
const seg = client.peekPendingSegmentGroups();
|
|
70
|
-
(0, node_assert_1.strict)(seg);
|
|
71
|
-
return { op, seg, refSeq: client.getCollabWindow().currentSeq };
|
|
72
|
-
}
|
|
73
|
-
disconnect(clientNames) {
|
|
74
|
-
const clientIdxs = new Set(clientNames.map((element) => this.idxFromName(element)));
|
|
75
|
-
for (const op of this.ops.splice(0))
|
|
76
|
-
for (const [i, c] of this.clients.all.entries()) {
|
|
77
|
-
if (clientIdxs.has(i)) {
|
|
78
|
-
this.perClientOps[i].push(op);
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
c.applyMsg(op);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
processAllOps() {
|
|
86
|
-
for (const op of this.ops.splice(0))
|
|
87
|
-
for (const c of this.clients.all) {
|
|
88
|
-
c.applyMsg(op);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
reconnect(clientNames) {
|
|
92
|
-
const clientIdxs = new Set(clientNames.map((element) => this.idxFromName(element)));
|
|
93
|
-
for (const [i, clientOps] of this.perClientOps.entries()) {
|
|
94
|
-
if (clientIdxs.has(i)) {
|
|
95
|
-
for (const op of clientOps.splice(0))
|
|
96
|
-
this.clients.all[i].applyMsg(op);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
submitDisconnectedOp(clientName, op) {
|
|
101
|
-
const client = this.clients[clientName];
|
|
102
|
-
this.ops.push(client.makeOpMessage(client.regeneratePendingOp(op.op, op.seg), ++this.seq));
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
exports.ReconnectTestHelper = ReconnectTestHelper;
|
|
106
|
-
//# sourceMappingURL=reconnectHelper.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"reconnectHelper.js","sourceRoot":"","sources":["../../src/test/reconnectHelper.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\n\nimport {\n\tendpointPosAndSide,\n\ttype IMergeTreeOptions,\n\ttype InteriorSequencePlace,\n\ttype SequencePlace,\n} from \"../index.js\";\nimport type { SegmentGroup } from \"../mergeTreeNodes.js\";\nimport {\n\tIMergeTreeDeltaOp,\n\ttype IMergeTreeInsertMsg,\n\ttype IMergeTreeObliterateMsg,\n\ttype IMergeTreeObliterateSidedMsg,\n\ttype IMergeTreeRemoveMsg,\n} from \"../ops.js\";\n\nimport type { TestClient } from \"./testClient.js\";\nimport { TestClientLogger, createClientsAtInitialState } from \"./testClientLogger.js\";\n\nconst ClientIds = [\"A\", \"B\", \"C\", \"D\"] as const;\ntype ClientName = (typeof ClientIds)[number];\n\nexport class ReconnectTestHelper {\n\tclients: Record<ClientName, TestClient> & { all: TestClient[] };\n\n\tidxFromName(name: ClientName): number {\n\t\treturn (name.codePointAt(0) ?? 0) - (\"A\".codePointAt(0) ?? 0);\n\t}\n\n\tlogger: TestClientLogger;\n\n\tops: ISequencedDocumentMessage[] = [];\n\tperClientOps: ISequencedDocumentMessage[][];\n\n\tseq: number = 0;\n\n\tpublic constructor(options: IMergeTreeOptions = {}) {\n\t\tthis.clients = createClientsAtInitialState(\n\t\t\t{\n\t\t\t\tinitialState: \"\",\n\t\t\t\toptions: {\n\t\t\t\t\tmergeTreeEnableObliterate: true,\n\t\t\t\t\tmergeTreeEnableObliterateReconnect: true,\n\t\t\t\t\t...options,\n\t\t\t\t},\n\t\t\t},\n\t\t\t...ClientIds,\n\t\t);\n\t\tthis.logger = new TestClientLogger(this.clients.all);\n\t\tthis.perClientOps = this.clients.all.map(() => []);\n\t}\n\n\tpublic insertText(clientName: ClientName, pos: number, text: string): void {\n\t\tconst client = this.clients[clientName];\n\t\tthis.ops.push(client.makeOpMessage(client.insertTextLocal(pos, text), ++this.seq));\n\t}\n\n\tpublic removeRange(clientName: ClientName, start: number, end: number): void {\n\t\tconst client = this.clients[clientName];\n\t\tthis.ops.push(client.makeOpMessage(client.removeRangeLocal(start, end), ++this.seq));\n\t}\n\n\tpublic obliterateRange(\n\t\tclientName: ClientName,\n\t\tstart: number | InteriorSequencePlace,\n\t\tend: number | InteriorSequencePlace,\n\t): void {\n\t\tconst client = this.clients[clientName];\n\t\tthis.ops.push(\n\t\t\tclient.makeOpMessage(\n\t\t\t\t// TODO: remove type assertions when sidedness is enabled\n\t\t\t\tclient.obliterateRangeLocal(start as number, end as number),\n\t\t\t\t++this.seq,\n\t\t\t),\n\t\t);\n\t}\n\n\tpublic insertTextLocal(\n\t\tclientName: ClientName,\n\t\tpos: number,\n\t\ttext: string,\n\t): {\n\t\top: IMergeTreeInsertMsg;\n\t\tseg: SegmentGroup;\n\t\trefSeq: number;\n\t} {\n\t\tconst client = this.clients[clientName];\n\t\tconst op = client.insertTextLocal(pos, text);\n\t\tassert(op);\n\t\tconst seg = client.peekPendingSegmentGroups();\n\t\tassert(seg);\n\t\treturn { op, seg, refSeq: client.getCollabWindow().currentSeq };\n\t}\n\n\tpublic removeRangeLocal(\n\t\tclientName: ClientName,\n\t\tstart: number,\n\t\tend: number,\n\t): {\n\t\top: IMergeTreeRemoveMsg;\n\t\tseg: SegmentGroup;\n\t\trefSeq: number;\n\t} {\n\t\tconst client = this.clients[clientName];\n\t\tconst op = client.removeRangeLocal(start, end);\n\t\tassert(op);\n\t\tconst seg = client.peekPendingSegmentGroups();\n\t\tassert(seg);\n\t\treturn { op, seg, refSeq: client.getCollabWindow().currentSeq };\n\t}\n\n\tpublic obliterateRangeLocal(\n\t\tclientName: ClientName,\n\t\tstart: SequencePlace,\n\t\tend: SequencePlace,\n\t): {\n\t\top: IMergeTreeObliterateMsg | IMergeTreeObliterateSidedMsg;\n\t\tseg: SegmentGroup;\n\t\trefSeq: number;\n\t} {\n\t\tconst client = this.clients[clientName];\n\t\tlet { startPos, endPos } = endpointPosAndSide(start, end);\n\t\tassert(\n\t\t\tstartPos !== undefined && endPos !== undefined,\n\t\t\t\"start and end positions must be defined\",\n\t\t);\n\t\tstartPos = startPos === \"start\" ? 0 : startPos;\n\t\tendPos = endPos === \"end\" ? client.getLength() : endPos;\n\t\tassert(\n\t\t\tstartPos !== \"end\" && endPos !== \"start\",\n\t\t\t\"start cannot be end and end cannot be start\",\n\t\t);\n\t\tconst op = client.obliterateRangeLocal(startPos, endPos);\n\t\tassert(op);\n\t\tconst seg = client.peekPendingSegmentGroups();\n\t\tassert(seg);\n\t\treturn { op, seg, refSeq: client.getCollabWindow().currentSeq };\n\t}\n\n\tpublic disconnect(clientNames: ClientName[]): void {\n\t\tconst clientIdxs = new Set(clientNames.map((element) => this.idxFromName(element)));\n\t\tfor (const op of this.ops.splice(0))\n\t\t\tfor (const [i, c] of this.clients.all.entries()) {\n\t\t\t\tif (clientIdxs.has(i)) {\n\t\t\t\t\tthis.perClientOps[i].push(op);\n\t\t\t\t} else {\n\t\t\t\t\tc.applyMsg(op);\n\t\t\t\t}\n\t\t\t}\n\t}\n\n\tpublic processAllOps(): void {\n\t\tfor (const op of this.ops.splice(0))\n\t\t\tfor (const c of this.clients.all) {\n\t\t\t\tc.applyMsg(op);\n\t\t\t}\n\t}\n\n\tpublic reconnect(clientNames: ClientName[]): void {\n\t\tconst clientIdxs = new Set(clientNames.map((element) => this.idxFromName(element)));\n\t\tfor (const [i, clientOps] of this.perClientOps.entries()) {\n\t\t\tif (clientIdxs.has(i)) {\n\t\t\t\tfor (const op of clientOps.splice(0)) this.clients.all[i].applyMsg(op);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic submitDisconnectedOp(\n\t\tclientName: ClientName,\n\t\top: { op: IMergeTreeDeltaOp; seg: SegmentGroup | SegmentGroup[] },\n\t): void {\n\t\tconst client = this.clients[clientName];\n\t\tthis.ops.push(client.makeOpMessage(client.regeneratePendingOp(op.op, op.seg), ++this.seq));\n\t}\n}\n"]}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
|
-
import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
6
|
-
import type { IMergeTreeOptions, InteriorSequencePlace } from "../index.js";
|
|
7
|
-
import type { TestClient } from "./testClient.js";
|
|
8
|
-
import { TestClientLogger } from "./testClientLogger.js";
|
|
9
|
-
declare const ClientIds: readonly ["A", "B", "C", "D"];
|
|
10
|
-
type ClientName = (typeof ClientIds)[number];
|
|
11
|
-
/**
|
|
12
|
-
* Like `ReconnectHelper`, but:
|
|
13
|
-
* - does not support reconnecting clients
|
|
14
|
-
* - supports advancing only some clients to a given sequence number (not all clients must be synchronized at the same time).
|
|
15
|
-
*
|
|
16
|
-
* This allows testing sequences of operations where clients have varying refSeqs, rather than having all clients advance refSeq
|
|
17
|
-
* in lockstep.
|
|
18
|
-
*/
|
|
19
|
-
export declare class PartialSyncTestHelper {
|
|
20
|
-
clients: Record<ClientName, TestClient> & {
|
|
21
|
-
all: TestClient[];
|
|
22
|
-
};
|
|
23
|
-
idxFromName(name: ClientName): number;
|
|
24
|
-
logger: TestClientLogger;
|
|
25
|
-
ops: ISequencedDocumentMessage[];
|
|
26
|
-
clientToLastAppliedSeq: Map<"A" | "C" | "B" | "D", number>;
|
|
27
|
-
perClientOps: ISequencedDocumentMessage[][];
|
|
28
|
-
private seq;
|
|
29
|
-
constructor(options?: IMergeTreeOptions);
|
|
30
|
-
private addMessage;
|
|
31
|
-
insertText(clientName: ClientName, pos: number, text: string): void;
|
|
32
|
-
removeRange(clientName: ClientName, start: number, end: number): void;
|
|
33
|
-
obliterateRange(clientName: ClientName, start: number | InteriorSequencePlace, end: number | InteriorSequencePlace): void;
|
|
34
|
-
advanceClientToSeq(clientName: ClientName, seq: number): void;
|
|
35
|
-
/**
|
|
36
|
-
* Sends all known ops to the procieded client ids.
|
|
37
|
-
*/
|
|
38
|
-
advanceClients(...clientNames: ClientName[]): void;
|
|
39
|
-
processAllOps(): void;
|
|
40
|
-
}
|
|
41
|
-
export {};
|
|
42
|
-
//# sourceMappingURL=partialSyncHelper.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"partialSyncHelper.d.ts","sourceRoot":"","sources":["../../src/test/partialSyncHelper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAExF,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAA+B,MAAM,uBAAuB,CAAC;AAEtF,QAAA,MAAM,SAAS,+BAAgC,CAAC;AAChD,KAAK,UAAU,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7C;;;;;;;GAOG;AACH,qBAAa,qBAAqB;IACjC,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG;QAAE,GAAG,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC;IAEhE,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM;IAIrC,MAAM,EAAE,gBAAgB,CAAC;IAEzB,GAAG,EAAE,yBAAyB,EAAE,CAAM;IACtC,sBAAsB,qCAAiC;IAEvD,YAAY,EAAE,yBAAyB,EAAE,EAAE,CAAC;IAE5C,OAAO,CAAC,GAAG,CAAa;gBAEL,OAAO,GAAE,iBAAsB;IAgBlD,OAAO,CAAC,UAAU;IAUX,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKnE,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAKrE,eAAe,CACrB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,MAAM,GAAG,qBAAqB,EACrC,GAAG,EAAE,MAAM,GAAG,qBAAqB,GACjC,IAAI;IAWA,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAwBpE;;OAEG;IACI,cAAc,CAAC,GAAG,WAAW,EAAE,UAAU,EAAE,GAAG,IAAI;IAOlD,aAAa,IAAI,IAAI;CAM5B"}
|