@fluidframework/sequence 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/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 +5 -9
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +38 -104
- package/dist/intervalCollection.js.map +1 -1
- package/dist/intervalCollectionMap.d.ts.map +1 -1
- package/dist/intervalCollectionMap.js +5 -4
- package/dist/intervalCollectionMap.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.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 +5 -9
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +39 -105
- package/lib/intervalCollection.js.map +1 -1
- package/lib/intervalCollectionMap.d.ts.map +1 -1
- package/lib/intervalCollectionMap.js +6 -5
- package/lib/intervalCollectionMap.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.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 +20 -19
- package/src/index.ts +0 -1
- package/src/intervalCollection.ts +59 -144
- package/src/intervalCollectionMap.ts +5 -5
- 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
|
@@ -17,18 +17,14 @@ 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";
|
|
@@ -62,13 +58,11 @@ import {
|
|
|
62
58
|
createPositionReferenceFromSegoff,
|
|
63
59
|
createSequenceInterval,
|
|
64
60
|
endReferenceSlidingPreference,
|
|
61
|
+
getSerializedProperties,
|
|
65
62
|
startReferenceSlidingPreference,
|
|
66
63
|
type ISerializableInterval,
|
|
67
|
-
type ISerializableIntervalPrivate,
|
|
68
64
|
} from "./intervals/index.js";
|
|
69
65
|
|
|
70
|
-
export const reservedIntervalIdKey = "intervalId";
|
|
71
|
-
|
|
72
66
|
export type ISerializedIntervalCollectionV1 = ISerializedInterval[];
|
|
73
67
|
|
|
74
68
|
export interface ISerializedIntervalCollectionV2 {
|
|
@@ -165,7 +159,6 @@ export function computeStickinessFromSide(
|
|
|
165
159
|
}
|
|
166
160
|
|
|
167
161
|
export class LocalIntervalCollection {
|
|
168
|
-
private static readonly legacyIdPrefix = "legacy";
|
|
169
162
|
public readonly overlappingIntervalsIndex: ISequenceOverlappingIntervalsIndex;
|
|
170
163
|
public readonly idIntervalIndex: IIdIntervalIndex;
|
|
171
164
|
public readonly endIntervalIndex: IEndpointIndex;
|
|
@@ -191,15 +184,6 @@ export class LocalIntervalCollection {
|
|
|
191
184
|
]);
|
|
192
185
|
}
|
|
193
186
|
|
|
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
187
|
/**
|
|
204
188
|
* Validates that a serialized interval has the ID property. Creates an ID
|
|
205
189
|
* if one does not already exist
|
|
@@ -207,27 +191,6 @@ export class LocalIntervalCollection {
|
|
|
207
191
|
* @param serializedInterval - The interval to be checked
|
|
208
192
|
* @returns The interval's existing or newly created id
|
|
209
193
|
*/
|
|
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
194
|
|
|
232
195
|
private removeIntervalFromIndexes(interval: SequenceIntervalClass) {
|
|
233
196
|
for (const index of this.indexes) {
|
|
@@ -248,54 +211,38 @@ export class LocalIntervalCollection {
|
|
|
248
211
|
this.removeIntervalListeners(interval);
|
|
249
212
|
}
|
|
250
213
|
|
|
251
|
-
public
|
|
214
|
+
public addInterval(
|
|
215
|
+
id: string,
|
|
252
216
|
start: SequencePlace,
|
|
253
217
|
end: SequencePlace,
|
|
254
|
-
|
|
218
|
+
props?: PropertySet,
|
|
255
219
|
op?: ISequencedDocumentMessage,
|
|
256
|
-
)
|
|
257
|
-
|
|
220
|
+
) {
|
|
221
|
+
// This check is intended to prevent scenarios where a random interval is created and then
|
|
222
|
+
// inserted into a collection. The aim is to ensure that the collection is created first
|
|
223
|
+
// then the user can create/add intervals based on the collection
|
|
224
|
+
if (
|
|
225
|
+
props?.[reservedRangeLabelsKey] !== undefined &&
|
|
226
|
+
props[reservedRangeLabelsKey][0] !== this.label
|
|
227
|
+
) {
|
|
228
|
+
throw new LoggingError(
|
|
229
|
+
"Adding an interval that belongs to another interval collection is not permitted",
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
const interval: SequenceIntervalClass = createSequenceInterval(
|
|
258
233
|
this.label,
|
|
234
|
+
id,
|
|
259
235
|
start,
|
|
260
236
|
end,
|
|
261
237
|
this.client,
|
|
262
|
-
|
|
238
|
+
IntervalType.SlideOnRemove,
|
|
263
239
|
op,
|
|
264
240
|
undefined,
|
|
265
241
|
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
242
|
+
props,
|
|
266
243
|
);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
public addInterval(
|
|
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
244
|
|
|
282
|
-
|
|
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
|
-
}
|
|
245
|
+
this.add(interval);
|
|
299
246
|
return interval;
|
|
300
247
|
}
|
|
301
248
|
|
|
@@ -1209,6 +1156,7 @@ export class IntervalCollection
|
|
|
1209
1156
|
getSlideToSegoff(
|
|
1210
1157
|
{ segment, offset },
|
|
1211
1158
|
undefined,
|
|
1159
|
+
createLocalReconnectingPerspective(this.client.getCurrentSeq(), clientId, localSeq),
|
|
1212
1160
|
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
1213
1161
|
) ?? segment;
|
|
1214
1162
|
|
|
@@ -1274,12 +1222,11 @@ export class IntervalCollection
|
|
|
1274
1222
|
);
|
|
1275
1223
|
if (this.savedSerializedIntervals) {
|
|
1276
1224
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
1277
|
-
|
|
1225
|
+
const { id, properties } = getSerializedProperties(serializedInterval);
|
|
1278
1226
|
const {
|
|
1279
1227
|
start: startPos,
|
|
1280
1228
|
end: endPos,
|
|
1281
1229
|
intervalType,
|
|
1282
|
-
properties,
|
|
1283
1230
|
startSide,
|
|
1284
1231
|
endSide,
|
|
1285
1232
|
} = serializedInterval;
|
|
@@ -1293,6 +1240,7 @@ export class IntervalCollection
|
|
|
1293
1240
|
: endPos;
|
|
1294
1241
|
const interval = createSequenceInterval(
|
|
1295
1242
|
label,
|
|
1243
|
+
id,
|
|
1296
1244
|
start,
|
|
1297
1245
|
end,
|
|
1298
1246
|
client,
|
|
@@ -1300,10 +1248,8 @@ export class IntervalCollection
|
|
|
1300
1248
|
undefined,
|
|
1301
1249
|
true,
|
|
1302
1250
|
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
1251
|
+
properties,
|
|
1303
1252
|
);
|
|
1304
|
-
if (properties) {
|
|
1305
|
-
interval.properties = addProperties(interval.properties, properties);
|
|
1306
|
-
}
|
|
1307
1253
|
this.localCollection.add(interval);
|
|
1308
1254
|
}
|
|
1309
1255
|
}
|
|
@@ -1345,7 +1291,7 @@ export class IntervalCollection
|
|
|
1345
1291
|
/**
|
|
1346
1292
|
* {@inheritdoc IIntervalCollection.getIntervalById}
|
|
1347
1293
|
*/
|
|
1348
|
-
public getIntervalById(id: string):
|
|
1294
|
+
public getIntervalById(id: string): SequenceIntervalClass | undefined {
|
|
1349
1295
|
if (!this.localCollection) {
|
|
1350
1296
|
throw new LoggingError("attach must be called before accessing intervals");
|
|
1351
1297
|
}
|
|
@@ -1367,10 +1313,12 @@ export class IntervalCollection
|
|
|
1367
1313
|
* {@inheritdoc IIntervalCollection.add}
|
|
1368
1314
|
*/
|
|
1369
1315
|
public add({
|
|
1316
|
+
id,
|
|
1370
1317
|
start,
|
|
1371
1318
|
end,
|
|
1372
1319
|
props,
|
|
1373
1320
|
}: {
|
|
1321
|
+
id?: string;
|
|
1374
1322
|
start: SequencePlace;
|
|
1375
1323
|
end: SequencePlace;
|
|
1376
1324
|
props?: PropertySet;
|
|
@@ -1389,14 +1337,12 @@ export class IntervalCollection
|
|
|
1389
1337
|
0x793 /* start and end cannot be undefined because they were not passed in as undefined */,
|
|
1390
1338
|
);
|
|
1391
1339
|
|
|
1392
|
-
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
1393
|
-
|
|
1394
1340
|
this.assertStickinessEnabled(start, end);
|
|
1395
1341
|
|
|
1396
1342
|
const interval: SequenceIntervalClass = this.localCollection.addInterval(
|
|
1343
|
+
id ?? uuid(),
|
|
1397
1344
|
toSequencePlace(startPos, startSide),
|
|
1398
1345
|
toSequencePlace(endPos, endSide),
|
|
1399
|
-
IntervalType.SlideOnRemove,
|
|
1400
1346
|
props,
|
|
1401
1347
|
);
|
|
1402
1348
|
|
|
@@ -1405,16 +1351,7 @@ export class IntervalCollection
|
|
|
1405
1351
|
setSlideOnRemove(interval.start);
|
|
1406
1352
|
setSlideOnRemove(interval.end);
|
|
1407
1353
|
}
|
|
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
|
-
};
|
|
1354
|
+
const serializedInterval: ISerializedInterval = interval.serialize();
|
|
1418
1355
|
const localSeq = this.getNextLocalSeq();
|
|
1419
1356
|
if (this.isCollaborating) {
|
|
1420
1357
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -1518,35 +1455,24 @@ export class IntervalCollection
|
|
|
1518
1455
|
let deltaProps: PropertySet | undefined;
|
|
1519
1456
|
let newInterval: SequenceIntervalClass | undefined;
|
|
1520
1457
|
if (props !== undefined) {
|
|
1521
|
-
interval.
|
|
1522
|
-
deltaProps = interval.propertyManager.handleProperties(
|
|
1523
|
-
{ props },
|
|
1524
|
-
interval,
|
|
1525
|
-
this.isCollaborating ? UnassignedSequenceNumber : UniversalSequenceNumber,
|
|
1526
|
-
UniversalSequenceNumber,
|
|
1527
|
-
true,
|
|
1528
|
-
);
|
|
1458
|
+
deltaProps = interval.changeProperties(props);
|
|
1529
1459
|
}
|
|
1530
|
-
|
|
1460
|
+
const changeEndpoints = start !== undefined && end !== undefined;
|
|
1461
|
+
if (changeEndpoints) {
|
|
1531
1462
|
newInterval = this.localCollection.changeInterval(interval, start, end);
|
|
1532
1463
|
if (!this.isCollaborating && newInterval !== undefined) {
|
|
1533
1464
|
setSlideOnRemove(newInterval.start);
|
|
1534
1465
|
setSlideOnRemove(newInterval.end);
|
|
1535
1466
|
}
|
|
1536
1467
|
}
|
|
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
1468
|
// Emit a property bag containing the ID and the other (if any) properties changed
|
|
1546
|
-
serializedInterval
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1469
|
+
const serializedInterval: SerializedIntervalDelta = (
|
|
1470
|
+
newInterval ?? interval
|
|
1471
|
+
).serializeDelta({
|
|
1472
|
+
props,
|
|
1473
|
+
includeEndpoints: changeEndpoints,
|
|
1474
|
+
});
|
|
1475
|
+
|
|
1550
1476
|
const localSeq = this.getNextLocalSeq();
|
|
1551
1477
|
if (this.isCollaborating) {
|
|
1552
1478
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -1615,7 +1541,7 @@ export class IntervalCollection
|
|
|
1615
1541
|
|
|
1616
1542
|
private removePendingChange(serializedInterval: SerializedIntervalDelta) {
|
|
1617
1543
|
// Change ops always have an ID.
|
|
1618
|
-
const id
|
|
1544
|
+
const { id } = getSerializedProperties(serializedInterval);
|
|
1619
1545
|
if (serializedInterval.start !== undefined) {
|
|
1620
1546
|
this.removePendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
1621
1547
|
}
|
|
@@ -1677,20 +1603,16 @@ export class IntervalCollection
|
|
|
1677
1603
|
// Note that the ID is in the property bag only to allow us to find the interval.
|
|
1678
1604
|
// This API cannot change the ID, and writing to the ID property will result in an exception. So we
|
|
1679
1605
|
// strip it out of the properties here.
|
|
1680
|
-
const {
|
|
1606
|
+
const { id, properties } = getSerializedProperties(serializedInterval);
|
|
1681
1607
|
assert(id !== undefined, 0x3fe /* id must exist on the interval */);
|
|
1682
|
-
const interval:
|
|
1608
|
+
const interval: SequenceIntervalClass | undefined = this.getIntervalById(id);
|
|
1683
1609
|
if (!interval) {
|
|
1684
1610
|
// The interval has been removed locally; no-op.
|
|
1685
1611
|
return;
|
|
1686
1612
|
}
|
|
1687
1613
|
|
|
1688
1614
|
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
|
-
});
|
|
1615
|
+
interval.ackPropertiesChange(properties, op);
|
|
1694
1616
|
|
|
1695
1617
|
this.ackInterval(interval, op);
|
|
1696
1618
|
} else {
|
|
@@ -1718,14 +1640,8 @@ export class IntervalCollection
|
|
|
1718
1640
|
op,
|
|
1719
1641
|
) ?? interval;
|
|
1720
1642
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
{ props: newProps },
|
|
1724
|
-
newInterval,
|
|
1725
|
-
op.sequenceNumber,
|
|
1726
|
-
op.minimumSequenceNumber,
|
|
1727
|
-
true,
|
|
1728
|
-
);
|
|
1643
|
+
const deltaProps = newInterval.changeProperties(properties, op);
|
|
1644
|
+
|
|
1729
1645
|
if (this.onDeserialize) {
|
|
1730
1646
|
this.onDeserialize(newInterval);
|
|
1731
1647
|
}
|
|
@@ -1734,7 +1650,7 @@ export class IntervalCollection
|
|
|
1734
1650
|
this.emitChange(newInterval, interval, local, false, op);
|
|
1735
1651
|
}
|
|
1736
1652
|
|
|
1737
|
-
const changedProperties = Object.keys(
|
|
1653
|
+
const changedProperties = Object.keys(properties).length > 0;
|
|
1738
1654
|
if (changedProperties) {
|
|
1739
1655
|
this.emit("propertyChanged", interval, deltaProps, local, op);
|
|
1740
1656
|
this.emit("changed", interval, deltaProps, undefined, local, false);
|
|
@@ -1780,12 +1696,11 @@ export class IntervalCollection
|
|
|
1780
1696
|
}
|
|
1781
1697
|
|
|
1782
1698
|
const { intervalType, properties, stickiness, startSide, endSide } = serializedInterval;
|
|
1783
|
-
|
|
1699
|
+
const { id } = getSerializedProperties(serializedInterval);
|
|
1784
1700
|
const { start: startRebased, end: endRebased } =
|
|
1785
1701
|
this.localSeqToRebasedInterval.get(localSeq) ?? this.computeRebasedPositions(localSeq);
|
|
1786
1702
|
|
|
1787
|
-
const
|
|
1788
|
-
const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(intervalId);
|
|
1703
|
+
const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(id);
|
|
1789
1704
|
|
|
1790
1705
|
const rebased: SerializedIntervalDelta = {
|
|
1791
1706
|
start: startRebased,
|
|
@@ -1801,10 +1716,10 @@ export class IntervalCollection
|
|
|
1801
1716
|
if (
|
|
1802
1717
|
opName === "change" &&
|
|
1803
1718
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- ?? is not logically equivalent when .hasPendingChangeStart returns false.
|
|
1804
|
-
(this.hasPendingChangeStart(
|
|
1719
|
+
(this.hasPendingChangeStart(id) || this.hasPendingChangeEnd(id))
|
|
1805
1720
|
) {
|
|
1806
1721
|
this.removePendingChange(serializedInterval);
|
|
1807
|
-
this.addPendingChange(
|
|
1722
|
+
this.addPendingChange(id, rebased);
|
|
1808
1723
|
}
|
|
1809
1724
|
|
|
1810
1725
|
// if the interval slid off the string, rebase the op to be a noop and delete the interval.
|
|
@@ -1850,6 +1765,7 @@ export class IntervalCollection
|
|
|
1850
1765
|
const newSegoff = getSlideToSegoff(
|
|
1851
1766
|
segoff,
|
|
1852
1767
|
slidingPreference,
|
|
1768
|
+
undefined,
|
|
1853
1769
|
this.options.mergeTreeReferencesCanSlideToEndpoint,
|
|
1854
1770
|
);
|
|
1855
1771
|
const value: { segment: ISegment | undefined; offset: number | undefined } | undefined =
|
|
@@ -1876,7 +1792,7 @@ export class IntervalCollection
|
|
|
1876
1792
|
endReferenceSlidingPreference(interval.stickiness),
|
|
1877
1793
|
);
|
|
1878
1794
|
|
|
1879
|
-
const id = interval.
|
|
1795
|
+
const id = interval.getIntervalId();
|
|
1880
1796
|
const hasPendingStartChange = this.hasPendingChangeStart(id);
|
|
1881
1797
|
const hasPendingEndChange = this.hasPendingChangeEnd(id);
|
|
1882
1798
|
|
|
@@ -1960,13 +1876,14 @@ export class IntervalCollection
|
|
|
1960
1876
|
op: ISequencedDocumentMessage,
|
|
1961
1877
|
localOpMetadata: IMapMessageLocalMetadata | undefined,
|
|
1962
1878
|
) {
|
|
1879
|
+
const { id, properties } = getSerializedProperties(serializedInterval);
|
|
1880
|
+
|
|
1963
1881
|
if (local) {
|
|
1964
1882
|
assert(
|
|
1965
1883
|
localOpMetadata !== undefined,
|
|
1966
1884
|
0x553 /* op metadata should be defined for local op */,
|
|
1967
1885
|
);
|
|
1968
1886
|
this.localSeqToSerializedInterval.delete(localOpMetadata.localSeq);
|
|
1969
|
-
const id: string = serializedInterval.properties?.[reservedIntervalIdKey];
|
|
1970
1887
|
const localInterval = this.getIntervalById(id);
|
|
1971
1888
|
if (localInterval) {
|
|
1972
1889
|
this.ackInterval(localInterval, op);
|
|
@@ -1978,13 +1895,11 @@ export class IntervalCollection
|
|
|
1978
1895
|
throw new LoggingError("attachSequence must be called");
|
|
1979
1896
|
}
|
|
1980
1897
|
|
|
1981
|
-
this.localCollection.ensureSerializedId(serializedInterval);
|
|
1982
|
-
|
|
1983
1898
|
const interval: SequenceIntervalClass = this.localCollection.addInterval(
|
|
1899
|
+
id,
|
|
1984
1900
|
toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? Side.Before),
|
|
1985
1901
|
toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? Side.Before),
|
|
1986
|
-
|
|
1987
|
-
serializedInterval.properties,
|
|
1902
|
+
properties,
|
|
1988
1903
|
op,
|
|
1989
1904
|
);
|
|
1990
1905
|
|
|
@@ -2015,7 +1930,7 @@ export class IntervalCollection
|
|
|
2015
1930
|
throw new LoggingError("attach must be called prior to deleting intervals");
|
|
2016
1931
|
}
|
|
2017
1932
|
|
|
2018
|
-
const id =
|
|
1933
|
+
const { id } = getSerializedProperties(serializedInterval);
|
|
2019
1934
|
const interval = this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
2020
1935
|
if (interval) {
|
|
2021
1936
|
this.deleteExistingInterval(interval, local, op);
|
|
@@ -14,7 +14,6 @@ import { makeSerializable } from "./IntervalCollectionValues.js";
|
|
|
14
14
|
import {
|
|
15
15
|
IntervalCollection,
|
|
16
16
|
opsMap,
|
|
17
|
-
reservedIntervalIdKey,
|
|
18
17
|
toOptionalSequencePlace,
|
|
19
18
|
toSequencePlace,
|
|
20
19
|
type ISerializedIntervalCollectionV1,
|
|
@@ -26,6 +25,7 @@ import {
|
|
|
26
25
|
ISerializableIntervalCollection,
|
|
27
26
|
SequenceOptions,
|
|
28
27
|
} from "./intervalCollectionMapInterfaces.js";
|
|
28
|
+
import { getSerializedProperties } from "./intervals/index.js";
|
|
29
29
|
|
|
30
30
|
function isMapOperation(op: unknown): op is IMapOperation {
|
|
31
31
|
return typeof op === "object" && op !== null && "type" in op && op.type === "act";
|
|
@@ -217,30 +217,30 @@ export class IntervalCollectionMap {
|
|
|
217
217
|
if (isMapOperation(op)) {
|
|
218
218
|
const { value, key } = op;
|
|
219
219
|
const map = this.get(key);
|
|
220
|
+
const { id, properties } = getSerializedProperties(value.value);
|
|
220
221
|
|
|
221
222
|
switch (value.opName) {
|
|
222
223
|
case "add": {
|
|
223
224
|
map.add({
|
|
225
|
+
id,
|
|
224
226
|
// Todo: we should improve typing so we know add ops always have start and end
|
|
225
227
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
226
228
|
start: toSequencePlace(value.value.start!, value.value.startSide),
|
|
227
229
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
228
230
|
end: toSequencePlace(value.value.end!, value.value.endSide),
|
|
229
|
-
props:
|
|
231
|
+
props: properties,
|
|
230
232
|
});
|
|
231
233
|
return true;
|
|
232
234
|
}
|
|
233
235
|
case "change": {
|
|
234
|
-
const { [reservedIntervalIdKey]: id, ...props } = value.value.properties ?? {};
|
|
235
236
|
map.change(id, {
|
|
236
237
|
start: toOptionalSequencePlace(value.value.start, value.value.startSide),
|
|
237
238
|
end: toOptionalSequencePlace(value.value.end, value.value.endSide),
|
|
238
|
-
props,
|
|
239
|
+
props: properties,
|
|
239
240
|
});
|
|
240
241
|
return true;
|
|
241
242
|
}
|
|
242
243
|
case "delete": {
|
|
243
|
-
const { [reservedIntervalIdKey]: id } = value.value.properties ?? {};
|
|
244
244
|
map.removeIntervalById(id);
|
|
245
245
|
return true;
|
|
246
246
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { Client, PropertyAction, RedBlackTree } from "@fluidframework/merge-tree/internal";
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { SequenceInterval, createTransientInterval } from "../intervals/index.js";
|
|
11
11
|
import { ISharedString } from "../sharedString.js";
|
|
12
12
|
|
|
13
13
|
import { type SequenceIntervalIndex } from "./intervalIndex.js";
|
|
@@ -77,21 +77,9 @@ export class EndpointInRangeIndex implements IEndpointInRangeIndex {
|
|
|
77
77
|
return true;
|
|
78
78
|
};
|
|
79
79
|
|
|
80
|
-
const transientStartInterval =
|
|
81
|
-
"transient",
|
|
82
|
-
start,
|
|
83
|
-
start,
|
|
84
|
-
this.client,
|
|
85
|
-
IntervalType.Transient,
|
|
86
|
-
);
|
|
80
|
+
const transientStartInterval = createTransientInterval(start, start, this.client);
|
|
87
81
|
|
|
88
|
-
const transientEndInterval =
|
|
89
|
-
"transient",
|
|
90
|
-
end,
|
|
91
|
-
end,
|
|
92
|
-
this.client,
|
|
93
|
-
IntervalType.Transient,
|
|
94
|
-
);
|
|
82
|
+
const transientEndInterval = createTransientInterval(end, end, this.client);
|
|
95
83
|
|
|
96
84
|
// Add comparison overrides to the transient intervals
|
|
97
85
|
(transientStartInterval as Partial<HasComparisonOverride>)[forceCompare] = -1;
|
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
/* eslint-disable import/no-deprecated */
|
|
7
|
-
|
|
8
6
|
import { Client, RedBlackTree } from "@fluidframework/merge-tree/internal";
|
|
9
7
|
|
|
10
|
-
import {
|
|
8
|
+
import { createTransientInterval, SequenceInterval } from "../intervals/index.js";
|
|
11
9
|
import { ISharedString } from "../sharedString.js";
|
|
12
10
|
|
|
13
11
|
import { type SequenceIntervalIndex } from "./intervalIndex.js";
|
|
@@ -39,13 +37,7 @@ export class EndpointIndex implements IEndpointIndex {
|
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
public previousInterval(pos: number): SequenceInterval | undefined {
|
|
42
|
-
const transientInterval =
|
|
43
|
-
"transient",
|
|
44
|
-
pos,
|
|
45
|
-
pos,
|
|
46
|
-
this.client,
|
|
47
|
-
IntervalType.Transient,
|
|
48
|
-
);
|
|
40
|
+
const transientInterval = createTransientInterval(pos, pos, this.client);
|
|
49
41
|
const rbNode = this.endIntervalTree.floor(transientInterval);
|
|
50
42
|
if (rbNode) {
|
|
51
43
|
return rbNode.data;
|
|
@@ -53,13 +45,7 @@ export class EndpointIndex implements IEndpointIndex {
|
|
|
53
45
|
}
|
|
54
46
|
|
|
55
47
|
public nextInterval(pos: number): SequenceInterval | undefined {
|
|
56
|
-
const transientInterval =
|
|
57
|
-
"transient",
|
|
58
|
-
pos,
|
|
59
|
-
pos,
|
|
60
|
-
this.client,
|
|
61
|
-
IntervalType.Transient,
|
|
62
|
-
);
|
|
48
|
+
const transientInterval = createTransientInterval(pos, pos, this.client);
|
|
63
49
|
const rbNode = this.endIntervalTree.ceil(transientInterval);
|
|
64
50
|
if (rbNode) {
|
|
65
51
|
return rbNode.data;
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
7
7
|
|
|
8
|
-
import { reservedIntervalIdKey } from "../intervalCollection.js";
|
|
9
8
|
import { type SequenceIntervalClass } from "../intervals/index.js";
|
|
10
9
|
|
|
11
10
|
import { type SequenceIntervalIndex } from "./intervalIndex.js";
|
|
@@ -26,12 +25,6 @@ class IdIntervalIndex implements IIdIntervalIndex, Iterable<SequenceIntervalClas
|
|
|
26
25
|
id !== undefined,
|
|
27
26
|
0x2c0 /* "ID must be created before adding interval to collection" */,
|
|
28
27
|
);
|
|
29
|
-
// Make the ID immutable.
|
|
30
|
-
Object.defineProperty(interval.properties, reservedIntervalIdKey, {
|
|
31
|
-
configurable: false,
|
|
32
|
-
enumerable: true,
|
|
33
|
-
writable: false,
|
|
34
|
-
});
|
|
35
28
|
this.intervalIdMap.set(id, interval);
|
|
36
29
|
}
|
|
37
30
|
|
|
@@ -14,9 +14,8 @@ import {
|
|
|
14
14
|
import { IntervalNode, IntervalTree } from "../intervalTree.js";
|
|
15
15
|
import {
|
|
16
16
|
ISerializableInterval,
|
|
17
|
-
IntervalType,
|
|
18
17
|
SequenceInterval,
|
|
19
|
-
|
|
18
|
+
createTransientInterval,
|
|
20
19
|
} from "../intervals/index.js";
|
|
21
20
|
import { ISharedString } from "../sharedString.js";
|
|
22
21
|
|
|
@@ -108,12 +107,10 @@ export class OverlappingIntervalsIndex implements ISequenceOverlappingIntervalsI
|
|
|
108
107
|
});
|
|
109
108
|
}
|
|
110
109
|
} else {
|
|
111
|
-
const transientInterval: SequenceInterval =
|
|
112
|
-
"transient",
|
|
110
|
+
const transientInterval: SequenceInterval = createTransientInterval(
|
|
113
111
|
start ?? "start",
|
|
114
112
|
end ?? "end",
|
|
115
113
|
this.client,
|
|
116
|
-
IntervalType.Transient,
|
|
117
114
|
);
|
|
118
115
|
|
|
119
116
|
if (start === undefined) {
|
|
@@ -184,13 +181,7 @@ export class OverlappingIntervalsIndex implements ISequenceOverlappingIntervalsI
|
|
|
184
181
|
) {
|
|
185
182
|
return [];
|
|
186
183
|
}
|
|
187
|
-
const transientInterval =
|
|
188
|
-
"transient",
|
|
189
|
-
start,
|
|
190
|
-
end,
|
|
191
|
-
this.client,
|
|
192
|
-
IntervalType.Transient,
|
|
193
|
-
);
|
|
184
|
+
const transientInterval = createTransientInterval(start, end, this.client);
|
|
194
185
|
|
|
195
186
|
const overlappingIntervalNodes = this.intervalTree.match(transientInterval);
|
|
196
187
|
return overlappingIntervalNodes.map((node) => node.key);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { Client, PropertyAction, RedBlackTree } from "@fluidframework/merge-tree/internal";
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { SequenceInterval, createTransientInterval } from "../intervals/index.js";
|
|
11
11
|
import { ISharedString } from "../sharedString.js";
|
|
12
12
|
|
|
13
13
|
import { type SequenceIntervalIndex } from "./intervalIndex.js";
|
|
@@ -76,21 +76,9 @@ export class StartpointInRangeIndex implements IStartpointInRangeIndex {
|
|
|
76
76
|
return true;
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
-
const transientStartInterval =
|
|
80
|
-
"transient",
|
|
81
|
-
start,
|
|
82
|
-
start,
|
|
83
|
-
this.client,
|
|
84
|
-
IntervalType.Transient,
|
|
85
|
-
);
|
|
79
|
+
const transientStartInterval = createTransientInterval(start, start, this.client);
|
|
86
80
|
|
|
87
|
-
const transientEndInterval =
|
|
88
|
-
"transient",
|
|
89
|
-
end,
|
|
90
|
-
end,
|
|
91
|
-
this.client,
|
|
92
|
-
IntervalType.Transient,
|
|
93
|
-
);
|
|
81
|
+
const transientEndInterval = createTransientInterval(end, end, this.client);
|
|
94
82
|
|
|
95
83
|
// Add comparison overrides to the transient intervals
|
|
96
84
|
(transientStartInterval as Partial<HasComparisonOverride>)[forceCompare] = -1;
|
package/src/intervals/index.ts
CHANGED
|
@@ -11,7 +11,6 @@ export {
|
|
|
11
11
|
IntervalType,
|
|
12
12
|
IntervalDeltaOpType,
|
|
13
13
|
IntervalStickiness,
|
|
14
|
-
ISerializableIntervalPrivate,
|
|
15
14
|
SerializedIntervalDelta,
|
|
16
15
|
CompressedSerializedInterval,
|
|
17
16
|
endReferenceSlidingPreference,
|
|
@@ -22,4 +21,6 @@ export {
|
|
|
22
21
|
SequenceIntervalClass,
|
|
23
22
|
createSequenceInterval,
|
|
24
23
|
createPositionReferenceFromSegoff,
|
|
24
|
+
createTransientInterval,
|
|
25
|
+
getSerializedProperties,
|
|
25
26
|
} from "./sequenceInterval.js";
|