@fluidframework/sequence 2.31.1 → 2.33.0-333010
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/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/intervalCollection.d.ts +11 -13
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +96 -149
- package/dist/intervalCollection.js.map +1 -1
- package/dist/intervalCollectionMap.d.ts +4 -4
- package/dist/intervalCollectionMap.d.ts.map +1 -1
- package/dist/intervalCollectionMap.js +16 -49
- package/dist/intervalCollectionMap.js.map +1 -1
- package/dist/intervalCollectionMapInterfaces.d.ts +21 -15
- package/dist/intervalCollectionMapInterfaces.d.ts.map +1 -1
- package/dist/intervalCollectionMapInterfaces.js.map +1 -1
- package/dist/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
- package/dist/intervalIndex/endpointInRangeIndex.js +2 -2
- package/dist/intervalIndex/endpointInRangeIndex.js.map +1 -1
- package/dist/intervalIndex/endpointIndex.d.ts.map +1 -1
- package/dist/intervalIndex/endpointIndex.js +2 -3
- package/dist/intervalIndex/endpointIndex.js.map +1 -1
- package/dist/intervalIndex/idIntervalIndex.d.ts.map +1 -1
- package/dist/intervalIndex/idIntervalIndex.js +0 -7
- package/dist/intervalIndex/idIntervalIndex.js.map +1 -1
- package/dist/intervalIndex/index.d.ts +0 -1
- package/dist/intervalIndex/index.d.ts.map +1 -1
- package/dist/intervalIndex/index.js +1 -3
- package/dist/intervalIndex/index.js.map +1 -1
- package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
- package/dist/intervalIndex/overlappingIntervalsIndex.js +2 -2
- package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
- package/dist/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
- package/dist/intervalIndex/startpointInRangeIndex.js +2 -2
- package/dist/intervalIndex/startpointInRangeIndex.js.map +1 -1
- package/dist/intervals/index.d.ts +2 -2
- package/dist/intervals/index.d.ts.map +1 -1
- package/dist/intervals/index.js +3 -1
- package/dist/intervals/index.js.map +1 -1
- package/dist/intervals/intervalUtils.d.ts +1 -5
- package/dist/intervals/intervalUtils.d.ts.map +1 -1
- package/dist/intervals/intervalUtils.js.map +1 -1
- package/dist/intervals/sequenceInterval.d.ts +20 -9
- package/dist/intervals/sequenceInterval.d.ts.map +1 -1
- package/dist/intervals/sequenceInterval.js +82 -27
- package/dist/intervals/sequenceInterval.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/revertibles.d.ts.map +1 -1
- package/dist/revertibles.js +8 -7
- package/dist/revertibles.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/intervalCollection.d.ts +11 -13
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +98 -151
- package/lib/intervalCollection.js.map +1 -1
- package/lib/intervalCollectionMap.d.ts +4 -4
- package/lib/intervalCollectionMap.d.ts.map +1 -1
- package/lib/intervalCollectionMap.js +17 -50
- package/lib/intervalCollectionMap.js.map +1 -1
- package/lib/intervalCollectionMapInterfaces.d.ts +21 -15
- package/lib/intervalCollectionMapInterfaces.d.ts.map +1 -1
- package/lib/intervalCollectionMapInterfaces.js.map +1 -1
- package/lib/intervalIndex/endpointInRangeIndex.d.ts.map +1 -1
- package/lib/intervalIndex/endpointInRangeIndex.js +3 -3
- package/lib/intervalIndex/endpointInRangeIndex.js.map +1 -1
- package/lib/intervalIndex/endpointIndex.d.ts.map +1 -1
- package/lib/intervalIndex/endpointIndex.js +3 -4
- package/lib/intervalIndex/endpointIndex.js.map +1 -1
- package/lib/intervalIndex/idIntervalIndex.d.ts.map +1 -1
- package/lib/intervalIndex/idIntervalIndex.js +0 -7
- package/lib/intervalIndex/idIntervalIndex.js.map +1 -1
- package/lib/intervalIndex/index.d.ts +0 -1
- package/lib/intervalIndex/index.d.ts.map +1 -1
- package/lib/intervalIndex/index.js +0 -1
- package/lib/intervalIndex/index.js.map +1 -1
- package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
- package/lib/intervalIndex/overlappingIntervalsIndex.js +3 -3
- package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
- package/lib/intervalIndex/startpointInRangeIndex.d.ts.map +1 -1
- package/lib/intervalIndex/startpointInRangeIndex.js +3 -3
- package/lib/intervalIndex/startpointInRangeIndex.js.map +1 -1
- package/lib/intervals/index.d.ts +2 -2
- package/lib/intervals/index.d.ts.map +1 -1
- package/lib/intervals/index.js +1 -1
- package/lib/intervals/index.js.map +1 -1
- package/lib/intervals/intervalUtils.d.ts +1 -5
- package/lib/intervals/intervalUtils.d.ts.map +1 -1
- package/lib/intervals/intervalUtils.js.map +1 -1
- package/lib/intervals/sequenceInterval.d.ts +20 -9
- package/lib/intervals/sequenceInterval.d.ts.map +1 -1
- package/lib/intervals/sequenceInterval.js +81 -28
- package/lib/intervals/sequenceInterval.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/revertibles.d.ts.map +1 -1
- package/lib/revertibles.js +8 -7
- package/lib/revertibles.js.map +1 -1
- package/package.json +19 -18
- package/src/index.ts +0 -1
- package/src/intervalCollection.ts +135 -198
- package/src/intervalCollectionMap.ts +19 -61
- package/src/intervalCollectionMapInterfaces.ts +33 -29
- package/src/intervalIndex/endpointInRangeIndex.ts +3 -15
- package/src/intervalIndex/endpointIndex.ts +3 -17
- package/src/intervalIndex/idIntervalIndex.ts +0 -7
- package/src/intervalIndex/index.ts +0 -1
- package/src/intervalIndex/overlappingIntervalsIndex.ts +3 -12
- package/src/intervalIndex/startpointInRangeIndex.ts +3 -15
- package/src/intervals/index.ts +2 -1
- package/src/intervals/intervalUtils.ts +0 -7
- package/src/intervals/sequenceInterval.ts +124 -33
- package/src/packageVersion.ts +1 -1
- package/src/revertibles.ts +8 -7
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +0 -11
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +0 -1
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +0 -38
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +0 -1
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +0 -11
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +0 -1
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js +0 -34
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +0 -1
- package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +0 -80
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
9
9
|
import { IEvent } from "@fluidframework/core-interfaces";
|
|
10
|
-
import { assert } from "@fluidframework/core-utils/internal";
|
|
10
|
+
import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
|
|
11
11
|
import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
12
12
|
import {
|
|
13
13
|
Client,
|
|
@@ -17,24 +17,19 @@ import {
|
|
|
17
17
|
PropertySet,
|
|
18
18
|
ReferenceType,
|
|
19
19
|
SlidingPreference,
|
|
20
|
-
UnassignedSequenceNumber,
|
|
21
|
-
UniversalSequenceNumber,
|
|
22
|
-
addProperties,
|
|
23
20
|
getSlideToSegoff,
|
|
24
21
|
refTypeIncludesFlag,
|
|
25
22
|
reservedRangeLabelsKey,
|
|
26
23
|
Side,
|
|
27
24
|
SequencePlace,
|
|
28
25
|
endpointPosAndSide,
|
|
29
|
-
PropertiesManager,
|
|
30
26
|
type ISegmentInternal,
|
|
31
|
-
|
|
27
|
+
createLocalReconnectingPerspective,
|
|
32
28
|
} from "@fluidframework/merge-tree/internal";
|
|
33
29
|
import { LoggingError, UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
34
30
|
import { v4 as uuid } from "uuid";
|
|
35
31
|
|
|
36
32
|
import {
|
|
37
|
-
IIntervalCollectionOperation,
|
|
38
33
|
IMapMessageLocalMetadata,
|
|
39
34
|
SequenceOptions,
|
|
40
35
|
type IIntervalCollectionTypeOperationValue,
|
|
@@ -53,7 +48,6 @@ import {
|
|
|
53
48
|
import {
|
|
54
49
|
CompressedSerializedInterval,
|
|
55
50
|
ISerializedInterval,
|
|
56
|
-
IntervalDeltaOpType,
|
|
57
51
|
IntervalStickiness,
|
|
58
52
|
IntervalType,
|
|
59
53
|
SequenceInterval,
|
|
@@ -62,13 +56,11 @@ import {
|
|
|
62
56
|
createPositionReferenceFromSegoff,
|
|
63
57
|
createSequenceInterval,
|
|
64
58
|
endReferenceSlidingPreference,
|
|
59
|
+
getSerializedProperties,
|
|
65
60
|
startReferenceSlidingPreference,
|
|
66
61
|
type ISerializableInterval,
|
|
67
|
-
type ISerializableIntervalPrivate,
|
|
68
62
|
} from "./intervals/index.js";
|
|
69
63
|
|
|
70
|
-
export const reservedIntervalIdKey = "intervalId";
|
|
71
|
-
|
|
72
64
|
export type ISerializedIntervalCollectionV1 = ISerializedInterval[];
|
|
73
65
|
|
|
74
66
|
export interface ISerializedIntervalCollectionV2 {
|
|
@@ -165,7 +157,6 @@ export function computeStickinessFromSide(
|
|
|
165
157
|
}
|
|
166
158
|
|
|
167
159
|
export class LocalIntervalCollection {
|
|
168
|
-
private static readonly legacyIdPrefix = "legacy";
|
|
169
160
|
public readonly overlappingIntervalsIndex: ISequenceOverlappingIntervalsIndex;
|
|
170
161
|
public readonly idIntervalIndex: IIdIntervalIndex;
|
|
171
162
|
public readonly endIntervalIndex: IEndpointIndex;
|
|
@@ -191,15 +182,6 @@ export class LocalIntervalCollection {
|
|
|
191
182
|
]);
|
|
192
183
|
}
|
|
193
184
|
|
|
194
|
-
public createLegacyId(
|
|
195
|
-
start: number | "start" | "end",
|
|
196
|
-
end: number | "start" | "end",
|
|
197
|
-
): string {
|
|
198
|
-
// Create a non-unique ID based on start and end to be used on intervals that come from legacy clients
|
|
199
|
-
// without ID's.
|
|
200
|
-
return `${LocalIntervalCollection.legacyIdPrefix}${start}-${end}`;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
185
|
/**
|
|
204
186
|
* Validates that a serialized interval has the ID property. Creates an ID
|
|
205
187
|
* if one does not already exist
|
|
@@ -207,27 +189,6 @@ export class LocalIntervalCollection {
|
|
|
207
189
|
* @param serializedInterval - The interval to be checked
|
|
208
190
|
* @returns The interval's existing or newly created id
|
|
209
191
|
*/
|
|
210
|
-
public ensureSerializedId(serializedInterval: ISerializedInterval): string {
|
|
211
|
-
let id: string | undefined = serializedInterval.properties?.[reservedIntervalIdKey];
|
|
212
|
-
if (id === undefined) {
|
|
213
|
-
// Back-compat: 0.39 and earlier did not have IDs on intervals. If an interval from such a client
|
|
214
|
-
// comes over the wire, create a non-unique one based on start/end.
|
|
215
|
-
// This will allow all clients to refer to this interval consistently.
|
|
216
|
-
id = this.createLegacyId(serializedInterval.start, serializedInterval.end);
|
|
217
|
-
const newProps = {
|
|
218
|
-
[reservedIntervalIdKey]: id,
|
|
219
|
-
};
|
|
220
|
-
serializedInterval.properties = addProperties(serializedInterval.properties, newProps);
|
|
221
|
-
}
|
|
222
|
-
// Make the ID immutable for safety's sake.
|
|
223
|
-
Object.defineProperty(serializedInterval.properties, reservedIntervalIdKey, {
|
|
224
|
-
configurable: false,
|
|
225
|
-
enumerable: true,
|
|
226
|
-
writable: false,
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
return id;
|
|
230
|
-
}
|
|
231
192
|
|
|
232
193
|
private removeIntervalFromIndexes(interval: SequenceIntervalClass) {
|
|
233
194
|
for (const index of this.indexes) {
|
|
@@ -248,54 +209,38 @@ export class LocalIntervalCollection {
|
|
|
248
209
|
this.removeIntervalListeners(interval);
|
|
249
210
|
}
|
|
250
211
|
|
|
251
|
-
public
|
|
212
|
+
public addInterval(
|
|
213
|
+
id: string,
|
|
252
214
|
start: SequencePlace,
|
|
253
215
|
end: SequencePlace,
|
|
254
|
-
|
|
216
|
+
props?: PropertySet,
|
|
255
217
|
op?: ISequencedDocumentMessage,
|
|
256
|
-
)
|
|
257
|
-
|
|
218
|
+
) {
|
|
219
|
+
// This check is intended to prevent scenarios where a random interval is created and then
|
|
220
|
+
// inserted into a collection. The aim is to ensure that the collection is created first
|
|
221
|
+
// then the user can create/add intervals based on the collection
|
|
222
|
+
if (
|
|
223
|
+
props?.[reservedRangeLabelsKey] !== undefined &&
|
|
224
|
+
props[reservedRangeLabelsKey][0] !== this.label
|
|
225
|
+
) {
|
|
226
|
+
throw new LoggingError(
|
|
227
|
+
"Adding an interval that belongs to another interval collection is not permitted",
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
const interval: SequenceIntervalClass = createSequenceInterval(
|
|
258
231
|
this.label,
|
|
232
|
+
id,
|
|
259
233
|
start,
|
|
260
234
|
end,
|
|
261
235
|
this.client,
|
|
262
|
-
|
|
236
|
+
IntervalType.SlideOnRemove,
|
|
263
237
|
op,
|
|
264
238
|
undefined,
|
|
265
239
|
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
240
|
+
props,
|
|
266
241
|
);
|
|
267
|
-
}
|
|
268
242
|
|
|
269
|
-
|
|
270
|
-
start: SequencePlace,
|
|
271
|
-
end: SequencePlace,
|
|
272
|
-
intervalType: IntervalType,
|
|
273
|
-
props?: PropertySet,
|
|
274
|
-
op?: ISequencedDocumentMessage,
|
|
275
|
-
) {
|
|
276
|
-
const interval: SequenceIntervalClass = this.createInterval(start, end, intervalType, op);
|
|
277
|
-
if (interval) {
|
|
278
|
-
if (!interval.properties) {
|
|
279
|
-
interval.properties = createMap<any>();
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (props) {
|
|
283
|
-
// This check is intended to prevent scenarios where a random interval is created and then
|
|
284
|
-
// inserted into a collection. The aim is to ensure that the collection is created first
|
|
285
|
-
// then the user can create/add intervals based on the collection
|
|
286
|
-
if (
|
|
287
|
-
props[reservedRangeLabelsKey] !== undefined &&
|
|
288
|
-
props[reservedRangeLabelsKey][0] !== this.label
|
|
289
|
-
) {
|
|
290
|
-
throw new LoggingError(
|
|
291
|
-
"Adding an interval that belongs to another interval collection is not permitted",
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
interval.properties = addProperties(interval.properties, props);
|
|
295
|
-
}
|
|
296
|
-
interval.properties[reservedIntervalIdKey] ??= uuid();
|
|
297
|
-
this.add(interval);
|
|
298
|
-
}
|
|
243
|
+
this.add(interval);
|
|
299
244
|
return interval;
|
|
300
245
|
}
|
|
301
246
|
|
|
@@ -405,55 +350,6 @@ export class LocalIntervalCollection {
|
|
|
405
350
|
}
|
|
406
351
|
}
|
|
407
352
|
|
|
408
|
-
const rebase: IIntervalCollectionOperation["rebase"] = (collection, op, localOpMetadata) => {
|
|
409
|
-
const { localSeq } = localOpMetadata;
|
|
410
|
-
const rebasedValue = collection.rebaseLocalInterval(op.opName, op.value, localSeq);
|
|
411
|
-
if (rebasedValue === undefined) {
|
|
412
|
-
return undefined;
|
|
413
|
-
}
|
|
414
|
-
const rebasedOp = { ...op, value: rebasedValue };
|
|
415
|
-
return { rebasedOp, rebasedLocalOpMetadata: localOpMetadata };
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
export const opsMap: Record<IntervalDeltaOpType, IIntervalCollectionOperation> = {
|
|
419
|
-
[IntervalDeltaOpType.ADD]: {
|
|
420
|
-
process: (collection, params, local, op, localOpMetadata) => {
|
|
421
|
-
// if params is undefined, the interval was deleted during
|
|
422
|
-
// rebasing
|
|
423
|
-
if (!params) {
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
assert(op !== undefined, 0x3fb /* op should exist here */);
|
|
427
|
-
collection.ackAdd(params, local, op, localOpMetadata);
|
|
428
|
-
},
|
|
429
|
-
rebase,
|
|
430
|
-
},
|
|
431
|
-
|
|
432
|
-
[IntervalDeltaOpType.DELETE]: {
|
|
433
|
-
process: (collection, params, local, op) => {
|
|
434
|
-
assert(op !== undefined, 0x3fc /* op should exist here */);
|
|
435
|
-
collection.ackDelete(params, local, op);
|
|
436
|
-
},
|
|
437
|
-
rebase: (collection, op, localOpMetadata) => {
|
|
438
|
-
// Deletion of intervals is based on id, so requires no rebasing.
|
|
439
|
-
return { rebasedOp: op, rebasedLocalOpMetadata: localOpMetadata };
|
|
440
|
-
},
|
|
441
|
-
},
|
|
442
|
-
|
|
443
|
-
[IntervalDeltaOpType.CHANGE]: {
|
|
444
|
-
process: (collection, params, local, op, localOpMetadata) => {
|
|
445
|
-
// if params is undefined, the interval was deleted during
|
|
446
|
-
// rebasing
|
|
447
|
-
if (!params) {
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
assert(op !== undefined, 0x3fd /* op should exist here */);
|
|
451
|
-
collection.ackChange(params, local, op, localOpMetadata);
|
|
452
|
-
},
|
|
453
|
-
rebase,
|
|
454
|
-
},
|
|
455
|
-
};
|
|
456
|
-
|
|
457
353
|
/**
|
|
458
354
|
* @legacy
|
|
459
355
|
* @alpha
|
|
@@ -1179,6 +1075,79 @@ export class IntervalCollection
|
|
|
1179
1075
|
return true;
|
|
1180
1076
|
}
|
|
1181
1077
|
|
|
1078
|
+
public process(
|
|
1079
|
+
op: IIntervalCollectionTypeOperationValue,
|
|
1080
|
+
local: boolean,
|
|
1081
|
+
message: ISequencedDocumentMessage,
|
|
1082
|
+
localOpMetadata: IMapMessageLocalMetadata,
|
|
1083
|
+
) {
|
|
1084
|
+
const { opName, value } = op;
|
|
1085
|
+
switch (opName) {
|
|
1086
|
+
case "add": {
|
|
1087
|
+
this.ackAdd(value, local, message, localOpMetadata);
|
|
1088
|
+
break;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
case "delete": {
|
|
1092
|
+
this.ackDelete(value, local, message);
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
case "change": {
|
|
1097
|
+
this.ackChange(value, local, message, localOpMetadata);
|
|
1098
|
+
break;
|
|
1099
|
+
}
|
|
1100
|
+
default:
|
|
1101
|
+
unreachableCase(opName);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
public resubmitMessage(
|
|
1106
|
+
op: IIntervalCollectionTypeOperationValue,
|
|
1107
|
+
localOpMetadata: IMapMessageLocalMetadata,
|
|
1108
|
+
): void {
|
|
1109
|
+
const { opName, value } = op;
|
|
1110
|
+
const { localSeq } = localOpMetadata;
|
|
1111
|
+
const rebasedValue =
|
|
1112
|
+
opName === "delete" ? value : this.rebaseLocalInterval(opName, value, localSeq);
|
|
1113
|
+
if (rebasedValue === undefined) {
|
|
1114
|
+
return undefined;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
this.submitDelta({ opName, value: rebasedValue as any }, localOpMetadata);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
public applyStashedOp(op: IIntervalCollectionTypeOperationValue): void {
|
|
1121
|
+
const { opName, value } = op;
|
|
1122
|
+
const { id, properties } = getSerializedProperties(value);
|
|
1123
|
+
switch (opName) {
|
|
1124
|
+
case "add": {
|
|
1125
|
+
this.add({
|
|
1126
|
+
id,
|
|
1127
|
+
// Todo: we should improve typing so we know add ops always have start and end
|
|
1128
|
+
start: toSequencePlace(value.start, value.startSide),
|
|
1129
|
+
end: toSequencePlace(value.end, value.endSide),
|
|
1130
|
+
props: properties,
|
|
1131
|
+
});
|
|
1132
|
+
break;
|
|
1133
|
+
}
|
|
1134
|
+
case "change": {
|
|
1135
|
+
this.change(id, {
|
|
1136
|
+
start: toOptionalSequencePlace(value.start, value.startSide),
|
|
1137
|
+
end: toOptionalSequencePlace(value.end, value.endSide),
|
|
1138
|
+
props: properties,
|
|
1139
|
+
});
|
|
1140
|
+
break;
|
|
1141
|
+
}
|
|
1142
|
+
case "delete": {
|
|
1143
|
+
this.removeIntervalById(id);
|
|
1144
|
+
break;
|
|
1145
|
+
}
|
|
1146
|
+
default:
|
|
1147
|
+
throw new Error("unknown ops should not be stashed");
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1182
1151
|
private rebasePositionWithSegmentSlide(
|
|
1183
1152
|
pos: number | "start" | "end",
|
|
1184
1153
|
seqNumberFrom: number,
|
|
@@ -1209,6 +1178,7 @@ export class IntervalCollection
|
|
|
1209
1178
|
getSlideToSegoff(
|
|
1210
1179
|
{ segment, offset },
|
|
1211
1180
|
undefined,
|
|
1181
|
+
createLocalReconnectingPerspective(this.client.getCurrentSeq(), clientId, localSeq),
|
|
1212
1182
|
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
1213
1183
|
) ?? segment;
|
|
1214
1184
|
|
|
@@ -1274,12 +1244,11 @@ export class IntervalCollection
|
|
|
1274
1244
|
);
|
|
1275
1245
|
if (this.savedSerializedIntervals) {
|
|
1276
1246
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
1277
|
-
|
|
1247
|
+
const { id, properties } = getSerializedProperties(serializedInterval);
|
|
1278
1248
|
const {
|
|
1279
1249
|
start: startPos,
|
|
1280
1250
|
end: endPos,
|
|
1281
1251
|
intervalType,
|
|
1282
|
-
properties,
|
|
1283
1252
|
startSide,
|
|
1284
1253
|
endSide,
|
|
1285
1254
|
} = serializedInterval;
|
|
@@ -1293,6 +1262,7 @@ export class IntervalCollection
|
|
|
1293
1262
|
: endPos;
|
|
1294
1263
|
const interval = createSequenceInterval(
|
|
1295
1264
|
label,
|
|
1265
|
+
id,
|
|
1296
1266
|
start,
|
|
1297
1267
|
end,
|
|
1298
1268
|
client,
|
|
@@ -1300,10 +1270,8 @@ export class IntervalCollection
|
|
|
1300
1270
|
undefined,
|
|
1301
1271
|
true,
|
|
1302
1272
|
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
1273
|
+
properties,
|
|
1303
1274
|
);
|
|
1304
|
-
if (properties) {
|
|
1305
|
-
interval.properties = addProperties(interval.properties, properties);
|
|
1306
|
-
}
|
|
1307
1275
|
this.localCollection.add(interval);
|
|
1308
1276
|
}
|
|
1309
1277
|
}
|
|
@@ -1345,7 +1313,7 @@ export class IntervalCollection
|
|
|
1345
1313
|
/**
|
|
1346
1314
|
* {@inheritdoc IIntervalCollection.getIntervalById}
|
|
1347
1315
|
*/
|
|
1348
|
-
public getIntervalById(id: string):
|
|
1316
|
+
public getIntervalById(id: string): SequenceIntervalClass | undefined {
|
|
1349
1317
|
if (!this.localCollection) {
|
|
1350
1318
|
throw new LoggingError("attach must be called before accessing intervals");
|
|
1351
1319
|
}
|
|
@@ -1367,10 +1335,12 @@ export class IntervalCollection
|
|
|
1367
1335
|
* {@inheritdoc IIntervalCollection.add}
|
|
1368
1336
|
*/
|
|
1369
1337
|
public add({
|
|
1338
|
+
id,
|
|
1370
1339
|
start,
|
|
1371
1340
|
end,
|
|
1372
1341
|
props,
|
|
1373
1342
|
}: {
|
|
1343
|
+
id?: string;
|
|
1374
1344
|
start: SequencePlace;
|
|
1375
1345
|
end: SequencePlace;
|
|
1376
1346
|
props?: PropertySet;
|
|
@@ -1389,14 +1359,12 @@ export class IntervalCollection
|
|
|
1389
1359
|
0x793 /* start and end cannot be undefined because they were not passed in as undefined */,
|
|
1390
1360
|
);
|
|
1391
1361
|
|
|
1392
|
-
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
1393
|
-
|
|
1394
1362
|
this.assertStickinessEnabled(start, end);
|
|
1395
1363
|
|
|
1396
1364
|
const interval: SequenceIntervalClass = this.localCollection.addInterval(
|
|
1365
|
+
id ?? uuid(),
|
|
1397
1366
|
toSequencePlace(startPos, startSide),
|
|
1398
1367
|
toSequencePlace(endPos, endSide),
|
|
1399
|
-
IntervalType.SlideOnRemove,
|
|
1400
1368
|
props,
|
|
1401
1369
|
);
|
|
1402
1370
|
|
|
@@ -1405,16 +1373,7 @@ export class IntervalCollection
|
|
|
1405
1373
|
setSlideOnRemove(interval.start);
|
|
1406
1374
|
setSlideOnRemove(interval.end);
|
|
1407
1375
|
}
|
|
1408
|
-
const serializedInterval: ISerializedInterval =
|
|
1409
|
-
start: startPos,
|
|
1410
|
-
end: endPos,
|
|
1411
|
-
intervalType: IntervalType.SlideOnRemove,
|
|
1412
|
-
properties: { ...interval.properties },
|
|
1413
|
-
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
1414
|
-
stickiness,
|
|
1415
|
-
startSide,
|
|
1416
|
-
endSide,
|
|
1417
|
-
};
|
|
1376
|
+
const serializedInterval: ISerializedInterval = interval.serialize();
|
|
1418
1377
|
const localSeq = this.getNextLocalSeq();
|
|
1419
1378
|
if (this.isCollaborating) {
|
|
1420
1379
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -1518,35 +1477,24 @@ export class IntervalCollection
|
|
|
1518
1477
|
let deltaProps: PropertySet | undefined;
|
|
1519
1478
|
let newInterval: SequenceIntervalClass | undefined;
|
|
1520
1479
|
if (props !== undefined) {
|
|
1521
|
-
interval.
|
|
1522
|
-
deltaProps = interval.propertyManager.handleProperties(
|
|
1523
|
-
{ props },
|
|
1524
|
-
interval,
|
|
1525
|
-
this.isCollaborating ? UnassignedSequenceNumber : UniversalSequenceNumber,
|
|
1526
|
-
UniversalSequenceNumber,
|
|
1527
|
-
true,
|
|
1528
|
-
);
|
|
1480
|
+
deltaProps = interval.changeProperties(props);
|
|
1529
1481
|
}
|
|
1530
|
-
|
|
1482
|
+
const changeEndpoints = start !== undefined && end !== undefined;
|
|
1483
|
+
if (changeEndpoints) {
|
|
1531
1484
|
newInterval = this.localCollection.changeInterval(interval, start, end);
|
|
1532
1485
|
if (!this.isCollaborating && newInterval !== undefined) {
|
|
1533
1486
|
setSlideOnRemove(newInterval.start);
|
|
1534
1487
|
setSlideOnRemove(newInterval.end);
|
|
1535
1488
|
}
|
|
1536
1489
|
}
|
|
1537
|
-
const serializedInterval: SerializedIntervalDelta = interval.serialize();
|
|
1538
|
-
const { startPos, startSide, endPos, endSide } = endpointPosAndSide(start, end);
|
|
1539
|
-
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
1540
|
-
serializedInterval.start = startPos;
|
|
1541
|
-
serializedInterval.end = endPos;
|
|
1542
|
-
serializedInterval.startSide = startSide;
|
|
1543
|
-
serializedInterval.endSide = endSide;
|
|
1544
|
-
serializedInterval.stickiness = stickiness;
|
|
1545
1490
|
// Emit a property bag containing the ID and the other (if any) properties changed
|
|
1546
|
-
serializedInterval
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1491
|
+
const serializedInterval: SerializedIntervalDelta = (
|
|
1492
|
+
newInterval ?? interval
|
|
1493
|
+
).serializeDelta({
|
|
1494
|
+
props,
|
|
1495
|
+
includeEndpoints: changeEndpoints,
|
|
1496
|
+
});
|
|
1497
|
+
|
|
1550
1498
|
const localSeq = this.getNextLocalSeq();
|
|
1551
1499
|
if (this.isCollaborating) {
|
|
1552
1500
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -1615,7 +1563,7 @@ export class IntervalCollection
|
|
|
1615
1563
|
|
|
1616
1564
|
private removePendingChange(serializedInterval: SerializedIntervalDelta) {
|
|
1617
1565
|
// Change ops always have an ID.
|
|
1618
|
-
const id
|
|
1566
|
+
const { id } = getSerializedProperties(serializedInterval);
|
|
1619
1567
|
if (serializedInterval.start !== undefined) {
|
|
1620
1568
|
this.removePendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
1621
1569
|
}
|
|
@@ -1655,7 +1603,7 @@ export class IntervalCollection
|
|
|
1655
1603
|
}
|
|
1656
1604
|
|
|
1657
1605
|
public ackChange(
|
|
1658
|
-
serializedInterval:
|
|
1606
|
+
serializedInterval: SerializedIntervalDelta,
|
|
1659
1607
|
local: boolean,
|
|
1660
1608
|
op: ISequencedDocumentMessage,
|
|
1661
1609
|
localOpMetadata: IMapMessageLocalMetadata | undefined,
|
|
@@ -1677,20 +1625,16 @@ export class IntervalCollection
|
|
|
1677
1625
|
// Note that the ID is in the property bag only to allow us to find the interval.
|
|
1678
1626
|
// This API cannot change the ID, and writing to the ID property will result in an exception. So we
|
|
1679
1627
|
// strip it out of the properties here.
|
|
1680
|
-
const {
|
|
1628
|
+
const { id, properties } = getSerializedProperties(serializedInterval);
|
|
1681
1629
|
assert(id !== undefined, 0x3fe /* id must exist on the interval */);
|
|
1682
|
-
const interval:
|
|
1630
|
+
const interval: SequenceIntervalClass | undefined = this.getIntervalById(id);
|
|
1683
1631
|
if (!interval) {
|
|
1684
1632
|
// The interval has been removed locally; no-op.
|
|
1685
1633
|
return;
|
|
1686
1634
|
}
|
|
1687
1635
|
|
|
1688
1636
|
if (local) {
|
|
1689
|
-
interval.
|
|
1690
|
-
// Let the propertyManager prune its pending change-properties set.
|
|
1691
|
-
interval.propertyManager.ack(op.sequenceNumber, op.minimumSequenceNumber, {
|
|
1692
|
-
props: newProps,
|
|
1693
|
-
});
|
|
1637
|
+
interval.ackPropertiesChange(properties, op);
|
|
1694
1638
|
|
|
1695
1639
|
this.ackInterval(interval, op);
|
|
1696
1640
|
} else {
|
|
@@ -1718,14 +1662,8 @@ export class IntervalCollection
|
|
|
1718
1662
|
op,
|
|
1719
1663
|
) ?? interval;
|
|
1720
1664
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
{ props: newProps },
|
|
1724
|
-
newInterval,
|
|
1725
|
-
op.sequenceNumber,
|
|
1726
|
-
op.minimumSequenceNumber,
|
|
1727
|
-
true,
|
|
1728
|
-
);
|
|
1665
|
+
const deltaProps = newInterval.changeProperties(properties, op);
|
|
1666
|
+
|
|
1729
1667
|
if (this.onDeserialize) {
|
|
1730
1668
|
this.onDeserialize(newInterval);
|
|
1731
1669
|
}
|
|
@@ -1734,7 +1672,7 @@ export class IntervalCollection
|
|
|
1734
1672
|
this.emitChange(newInterval, interval, local, false, op);
|
|
1735
1673
|
}
|
|
1736
1674
|
|
|
1737
|
-
const changedProperties = Object.keys(
|
|
1675
|
+
const changedProperties = Object.keys(properties).length > 0;
|
|
1738
1676
|
if (changedProperties) {
|
|
1739
1677
|
this.emit("propertyChanged", interval, deltaProps, local, op);
|
|
1740
1678
|
this.emit("changed", interval, deltaProps, undefined, local, false);
|
|
@@ -1780,12 +1718,11 @@ export class IntervalCollection
|
|
|
1780
1718
|
}
|
|
1781
1719
|
|
|
1782
1720
|
const { intervalType, properties, stickiness, startSide, endSide } = serializedInterval;
|
|
1783
|
-
|
|
1721
|
+
const { id } = getSerializedProperties(serializedInterval);
|
|
1784
1722
|
const { start: startRebased, end: endRebased } =
|
|
1785
1723
|
this.localSeqToRebasedInterval.get(localSeq) ?? this.computeRebasedPositions(localSeq);
|
|
1786
1724
|
|
|
1787
|
-
const
|
|
1788
|
-
const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(intervalId);
|
|
1725
|
+
const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(id);
|
|
1789
1726
|
|
|
1790
1727
|
const rebased: SerializedIntervalDelta = {
|
|
1791
1728
|
start: startRebased,
|
|
@@ -1801,10 +1738,10 @@ export class IntervalCollection
|
|
|
1801
1738
|
if (
|
|
1802
1739
|
opName === "change" &&
|
|
1803
1740
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- ?? is not logically equivalent when .hasPendingChangeStart returns false.
|
|
1804
|
-
(this.hasPendingChangeStart(
|
|
1741
|
+
(this.hasPendingChangeStart(id) || this.hasPendingChangeEnd(id))
|
|
1805
1742
|
) {
|
|
1806
1743
|
this.removePendingChange(serializedInterval);
|
|
1807
|
-
this.addPendingChange(
|
|
1744
|
+
this.addPendingChange(id, rebased);
|
|
1808
1745
|
}
|
|
1809
1746
|
|
|
1810
1747
|
// if the interval slid off the string, rebase the op to be a noop and delete the interval.
|
|
@@ -1850,6 +1787,7 @@ export class IntervalCollection
|
|
|
1850
1787
|
const newSegoff = getSlideToSegoff(
|
|
1851
1788
|
segoff,
|
|
1852
1789
|
slidingPreference,
|
|
1790
|
+
undefined,
|
|
1853
1791
|
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
1854
1792
|
);
|
|
1855
1793
|
const value: { segment: ISegment | undefined; offset: number | undefined } | undefined =
|
|
@@ -1876,7 +1814,7 @@ export class IntervalCollection
|
|
|
1876
1814
|
endReferenceSlidingPreference(interval.stickiness),
|
|
1877
1815
|
);
|
|
1878
1816
|
|
|
1879
|
-
const id = interval.
|
|
1817
|
+
const id = interval.getIntervalId();
|
|
1880
1818
|
const hasPendingStartChange = this.hasPendingChangeStart(id);
|
|
1881
1819
|
const hasPendingEndChange = this.hasPendingChangeEnd(id);
|
|
1882
1820
|
|
|
@@ -1960,13 +1898,14 @@ export class IntervalCollection
|
|
|
1960
1898
|
op: ISequencedDocumentMessage,
|
|
1961
1899
|
localOpMetadata: IMapMessageLocalMetadata | undefined,
|
|
1962
1900
|
) {
|
|
1901
|
+
const { id, properties } = getSerializedProperties(serializedInterval);
|
|
1902
|
+
|
|
1963
1903
|
if (local) {
|
|
1964
1904
|
assert(
|
|
1965
1905
|
localOpMetadata !== undefined,
|
|
1966
1906
|
0x553 /* op metadata should be defined for local op */,
|
|
1967
1907
|
);
|
|
1968
1908
|
this.localSeqToSerializedInterval.delete(localOpMetadata.localSeq);
|
|
1969
|
-
const id: string = serializedInterval.properties?.[reservedIntervalIdKey];
|
|
1970
1909
|
const localInterval = this.getIntervalById(id);
|
|
1971
1910
|
if (localInterval) {
|
|
1972
1911
|
this.ackInterval(localInterval, op);
|
|
@@ -1978,13 +1917,11 @@ export class IntervalCollection
|
|
|
1978
1917
|
throw new LoggingError("attachSequence must be called");
|
|
1979
1918
|
}
|
|
1980
1919
|
|
|
1981
|
-
this.localCollection.ensureSerializedId(serializedInterval);
|
|
1982
|
-
|
|
1983
1920
|
const interval: SequenceIntervalClass = this.localCollection.addInterval(
|
|
1921
|
+
id,
|
|
1984
1922
|
toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? Side.Before),
|
|
1985
1923
|
toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? Side.Before),
|
|
1986
|
-
|
|
1987
|
-
serializedInterval.properties,
|
|
1924
|
+
properties,
|
|
1988
1925
|
op,
|
|
1989
1926
|
);
|
|
1990
1927
|
|
|
@@ -2000,7 +1937,7 @@ export class IntervalCollection
|
|
|
2000
1937
|
}
|
|
2001
1938
|
|
|
2002
1939
|
public ackDelete(
|
|
2003
|
-
serializedInterval:
|
|
1940
|
+
serializedInterval: SerializedIntervalDelta,
|
|
2004
1941
|
local: boolean,
|
|
2005
1942
|
op: ISequencedDocumentMessage,
|
|
2006
1943
|
): void {
|
|
@@ -2015,7 +1952,7 @@ export class IntervalCollection
|
|
|
2015
1952
|
throw new LoggingError("attach must be called prior to deleting intervals");
|
|
2016
1953
|
}
|
|
2017
1954
|
|
|
2018
|
-
const id =
|
|
1955
|
+
const { id } = getSerializedProperties(serializedInterval);
|
|
2019
1956
|
const interval = this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
2020
1957
|
if (interval) {
|
|
2021
1958
|
this.deleteExistingInterval(interval, local, op);
|