@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
|
@@ -4,13 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/* eslint-disable no-bitwise */
|
|
6
6
|
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
7
|
-
import { assert } from "@fluidframework/core-utils/internal";
|
|
8
|
-
import { DetachedReferencePosition, ReferenceType, SlidingPreference,
|
|
7
|
+
import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
|
|
8
|
+
import { DetachedReferencePosition, ReferenceType, SlidingPreference, getSlideToSegoff, refTypeIncludesFlag, reservedRangeLabelsKey, Side, endpointPosAndSide, createLocalReconnectingPerspective, } from "@fluidframework/merge-tree/internal";
|
|
9
9
|
import { LoggingError, UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
10
10
|
import { v4 as uuid } from "uuid";
|
|
11
11
|
import { createIdIntervalIndex, EndpointIndex, OverlappingIntervalsIndex, } from "./intervalIndex/index.js";
|
|
12
|
-
import {
|
|
13
|
-
export const reservedIntervalIdKey = "intervalId";
|
|
12
|
+
import { IntervalStickiness, IntervalType, createPositionReferenceFromSegoff, createSequenceInterval, endReferenceSlidingPreference, getSerializedProperties, startReferenceSlidingPreference, } from "./intervals/index.js";
|
|
14
13
|
export function sidesFromStickiness(stickiness) {
|
|
15
14
|
const startSide = (stickiness & IntervalStickiness.START) !== 0 ? Side.After : Side.Before;
|
|
16
15
|
const endSide = (stickiness & IntervalStickiness.END) !== 0 ? Side.Before : Side.After;
|
|
@@ -88,11 +87,6 @@ export class LocalIntervalCollection {
|
|
|
88
87
|
this.endIntervalIndex,
|
|
89
88
|
]);
|
|
90
89
|
}
|
|
91
|
-
createLegacyId(start, end) {
|
|
92
|
-
// Create a non-unique ID based on start and end to be used on intervals that come from legacy clients
|
|
93
|
-
// without ID's.
|
|
94
|
-
return `${LocalIntervalCollection.legacyIdPrefix}${start}-${end}`;
|
|
95
|
-
}
|
|
96
90
|
/**
|
|
97
91
|
* Validates that a serialized interval has the ID property. Creates an ID
|
|
98
92
|
* if one does not already exist
|
|
@@ -100,26 +94,6 @@ export class LocalIntervalCollection {
|
|
|
100
94
|
* @param serializedInterval - The interval to be checked
|
|
101
95
|
* @returns The interval's existing or newly created id
|
|
102
96
|
*/
|
|
103
|
-
ensureSerializedId(serializedInterval) {
|
|
104
|
-
let id = serializedInterval.properties?.[reservedIntervalIdKey];
|
|
105
|
-
if (id === undefined) {
|
|
106
|
-
// Back-compat: 0.39 and earlier did not have IDs on intervals. If an interval from such a client
|
|
107
|
-
// comes over the wire, create a non-unique one based on start/end.
|
|
108
|
-
// This will allow all clients to refer to this interval consistently.
|
|
109
|
-
id = this.createLegacyId(serializedInterval.start, serializedInterval.end);
|
|
110
|
-
const newProps = {
|
|
111
|
-
[reservedIntervalIdKey]: id,
|
|
112
|
-
};
|
|
113
|
-
serializedInterval.properties = addProperties(serializedInterval.properties, newProps);
|
|
114
|
-
}
|
|
115
|
-
// Make the ID immutable for safety's sake.
|
|
116
|
-
Object.defineProperty(serializedInterval.properties, reservedIntervalIdKey, {
|
|
117
|
-
configurable: false,
|
|
118
|
-
enumerable: true,
|
|
119
|
-
writable: false,
|
|
120
|
-
});
|
|
121
|
-
return id;
|
|
122
|
-
}
|
|
123
97
|
removeIntervalFromIndexes(interval) {
|
|
124
98
|
for (const index of this.indexes) {
|
|
125
99
|
index.remove(interval);
|
|
@@ -135,28 +109,16 @@ export class LocalIntervalCollection {
|
|
|
135
109
|
this.removeIntervalFromIndexes(interval);
|
|
136
110
|
this.removeIntervalListeners(interval);
|
|
137
111
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
interval.properties = createMap();
|
|
146
|
-
}
|
|
147
|
-
if (props) {
|
|
148
|
-
// This check is intended to prevent scenarios where a random interval is created and then
|
|
149
|
-
// inserted into a collection. The aim is to ensure that the collection is created first
|
|
150
|
-
// then the user can create/add intervals based on the collection
|
|
151
|
-
if (props[reservedRangeLabelsKey] !== undefined &&
|
|
152
|
-
props[reservedRangeLabelsKey][0] !== this.label) {
|
|
153
|
-
throw new LoggingError("Adding an interval that belongs to another interval collection is not permitted");
|
|
154
|
-
}
|
|
155
|
-
interval.properties = addProperties(interval.properties, props);
|
|
156
|
-
}
|
|
157
|
-
interval.properties[reservedIntervalIdKey] ??= uuid();
|
|
158
|
-
this.add(interval);
|
|
112
|
+
addInterval(id, start, end, props, op) {
|
|
113
|
+
// This check is intended to prevent scenarios where a random interval is created and then
|
|
114
|
+
// inserted into a collection. The aim is to ensure that the collection is created first
|
|
115
|
+
// then the user can create/add intervals based on the collection
|
|
116
|
+
if (props?.[reservedRangeLabelsKey] !== undefined &&
|
|
117
|
+
props[reservedRangeLabelsKey][0] !== this.label) {
|
|
118
|
+
throw new LoggingError("Adding an interval that belongs to another interval collection is not permitted");
|
|
159
119
|
}
|
|
120
|
+
const interval = createSequenceInterval(this.label, id, start, end, this.client, IntervalType.SlideOnRemove, op, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint, props);
|
|
121
|
+
this.add(interval);
|
|
160
122
|
return interval;
|
|
161
123
|
}
|
|
162
124
|
linkEndpointsToInterval(interval) {
|
|
@@ -227,52 +189,6 @@ export class LocalIntervalCollection {
|
|
|
227
189
|
interval.removePositionChangeListeners();
|
|
228
190
|
}
|
|
229
191
|
}
|
|
230
|
-
LocalIntervalCollection.legacyIdPrefix = "legacy";
|
|
231
|
-
const rebase = (collection, op, localOpMetadata) => {
|
|
232
|
-
const { localSeq } = localOpMetadata;
|
|
233
|
-
const rebasedValue = collection.rebaseLocalInterval(op.opName, op.value, localSeq);
|
|
234
|
-
if (rebasedValue === undefined) {
|
|
235
|
-
return undefined;
|
|
236
|
-
}
|
|
237
|
-
const rebasedOp = { ...op, value: rebasedValue };
|
|
238
|
-
return { rebasedOp, rebasedLocalOpMetadata: localOpMetadata };
|
|
239
|
-
};
|
|
240
|
-
export const opsMap = {
|
|
241
|
-
[IntervalDeltaOpType.ADD]: {
|
|
242
|
-
process: (collection, params, local, op, localOpMetadata) => {
|
|
243
|
-
// if params is undefined, the interval was deleted during
|
|
244
|
-
// rebasing
|
|
245
|
-
if (!params) {
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
assert(op !== undefined, 0x3fb /* op should exist here */);
|
|
249
|
-
collection.ackAdd(params, local, op, localOpMetadata);
|
|
250
|
-
},
|
|
251
|
-
rebase,
|
|
252
|
-
},
|
|
253
|
-
[IntervalDeltaOpType.DELETE]: {
|
|
254
|
-
process: (collection, params, local, op) => {
|
|
255
|
-
assert(op !== undefined, 0x3fc /* op should exist here */);
|
|
256
|
-
collection.ackDelete(params, local, op);
|
|
257
|
-
},
|
|
258
|
-
rebase: (collection, op, localOpMetadata) => {
|
|
259
|
-
// Deletion of intervals is based on id, so requires no rebasing.
|
|
260
|
-
return { rebasedOp: op, rebasedLocalOpMetadata: localOpMetadata };
|
|
261
|
-
},
|
|
262
|
-
},
|
|
263
|
-
[IntervalDeltaOpType.CHANGE]: {
|
|
264
|
-
process: (collection, params, local, op, localOpMetadata) => {
|
|
265
|
-
// if params is undefined, the interval was deleted during
|
|
266
|
-
// rebasing
|
|
267
|
-
if (!params) {
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
assert(op !== undefined, 0x3fd /* op should exist here */);
|
|
271
|
-
collection.ackChange(params, local, op, localOpMetadata);
|
|
272
|
-
},
|
|
273
|
-
rebase,
|
|
274
|
-
},
|
|
275
|
-
};
|
|
276
192
|
class IntervalCollectionIterator {
|
|
277
193
|
constructor(collection, iteratesForward = true, start, end) {
|
|
278
194
|
this.results = [];
|
|
@@ -339,6 +255,64 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
339
255
|
}
|
|
340
256
|
return true;
|
|
341
257
|
}
|
|
258
|
+
process(op, local, message, localOpMetadata) {
|
|
259
|
+
const { opName, value } = op;
|
|
260
|
+
switch (opName) {
|
|
261
|
+
case "add": {
|
|
262
|
+
this.ackAdd(value, local, message, localOpMetadata);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
case "delete": {
|
|
266
|
+
this.ackDelete(value, local, message);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
case "change": {
|
|
270
|
+
this.ackChange(value, local, message, localOpMetadata);
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
default:
|
|
274
|
+
unreachableCase(opName);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
resubmitMessage(op, localOpMetadata) {
|
|
278
|
+
const { opName, value } = op;
|
|
279
|
+
const { localSeq } = localOpMetadata;
|
|
280
|
+
const rebasedValue = opName === "delete" ? value : this.rebaseLocalInterval(opName, value, localSeq);
|
|
281
|
+
if (rebasedValue === undefined) {
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
this.submitDelta({ opName, value: rebasedValue }, localOpMetadata);
|
|
285
|
+
}
|
|
286
|
+
applyStashedOp(op) {
|
|
287
|
+
const { opName, value } = op;
|
|
288
|
+
const { id, properties } = getSerializedProperties(value);
|
|
289
|
+
switch (opName) {
|
|
290
|
+
case "add": {
|
|
291
|
+
this.add({
|
|
292
|
+
id,
|
|
293
|
+
// Todo: we should improve typing so we know add ops always have start and end
|
|
294
|
+
start: toSequencePlace(value.start, value.startSide),
|
|
295
|
+
end: toSequencePlace(value.end, value.endSide),
|
|
296
|
+
props: properties,
|
|
297
|
+
});
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
case "change": {
|
|
301
|
+
this.change(id, {
|
|
302
|
+
start: toOptionalSequencePlace(value.start, value.startSide),
|
|
303
|
+
end: toOptionalSequencePlace(value.end, value.endSide),
|
|
304
|
+
props: properties,
|
|
305
|
+
});
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
case "delete": {
|
|
309
|
+
this.removeIntervalById(id);
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
default:
|
|
313
|
+
throw new Error("unknown ops should not be stashed");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
342
316
|
rebasePositionWithSegmentSlide(pos, seqNumberFrom, localSeq) {
|
|
343
317
|
if (!this.client) {
|
|
344
318
|
throw new LoggingError("mergeTree client must exist");
|
|
@@ -353,7 +327,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
353
327
|
}, localSeq);
|
|
354
328
|
// if segment is undefined, it slid off the string
|
|
355
329
|
assert(segment !== undefined, 0x54e /* No segment found */);
|
|
356
|
-
const segoff = getSlideToSegoff({ segment, offset }, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint) ?? segment;
|
|
330
|
+
const segoff = getSlideToSegoff({ segment, offset }, undefined, createLocalReconnectingPerspective(this.client.getCurrentSeq(), clientId, localSeq), this.options.mergeTreeReferencesCanSlideToEndpoint) ?? segment;
|
|
357
331
|
// case happens when rebasing op, but concurrently entire string has been deleted
|
|
358
332
|
if (segoff.segment === undefined || segoff.offset === undefined) {
|
|
359
333
|
return DetachedReferencePosition;
|
|
@@ -394,18 +368,15 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
394
368
|
this.localCollection = new LocalIntervalCollection(client, label, this.options, (interval, previousInterval) => this.emitChange(interval, previousInterval, true, true));
|
|
395
369
|
if (this.savedSerializedIntervals) {
|
|
396
370
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
397
|
-
|
|
398
|
-
const { start: startPos, end: endPos, intervalType,
|
|
371
|
+
const { id, properties } = getSerializedProperties(serializedInterval);
|
|
372
|
+
const { start: startPos, end: endPos, intervalType, startSide, endSide, } = serializedInterval;
|
|
399
373
|
const start = typeof startPos === "number" && startSide !== undefined
|
|
400
374
|
? { pos: startPos, side: startSide }
|
|
401
375
|
: startPos;
|
|
402
376
|
const end = typeof endPos === "number" && endSide !== undefined
|
|
403
377
|
? { pos: endPos, side: endSide }
|
|
404
378
|
: endPos;
|
|
405
|
-
const interval = createSequenceInterval(label, start, end, client, intervalType, undefined, true, this.options.mergeTreeReferencesCanSlideToEndpoint);
|
|
406
|
-
if (properties) {
|
|
407
|
-
interval.properties = addProperties(interval.properties, properties);
|
|
408
|
-
}
|
|
379
|
+
const interval = createSequenceInterval(label, id, start, end, client, intervalType, undefined, true, this.options.mergeTreeReferencesCanSlideToEndpoint, properties);
|
|
409
380
|
this.localCollection.add(interval);
|
|
410
381
|
}
|
|
411
382
|
}
|
|
@@ -451,7 +422,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
451
422
|
/**
|
|
452
423
|
* {@inheritdoc IIntervalCollection.add}
|
|
453
424
|
*/
|
|
454
|
-
add({ start, end, props, }) {
|
|
425
|
+
add({ id, start, end, props, }) {
|
|
455
426
|
if (!this.localCollection) {
|
|
456
427
|
throw new LoggingError("attach must be called prior to adding intervals");
|
|
457
428
|
}
|
|
@@ -460,24 +431,14 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
460
431
|
endPos !== undefined &&
|
|
461
432
|
startSide !== undefined &&
|
|
462
433
|
endSide !== undefined, 0x793 /* start and end cannot be undefined because they were not passed in as undefined */);
|
|
463
|
-
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
464
434
|
this.assertStickinessEnabled(start, end);
|
|
465
|
-
const interval = this.localCollection.addInterval(toSequencePlace(startPos, startSide), toSequencePlace(endPos, endSide),
|
|
435
|
+
const interval = this.localCollection.addInterval(id ?? uuid(), toSequencePlace(startPos, startSide), toSequencePlace(endPos, endSide), props);
|
|
466
436
|
if (interval) {
|
|
467
437
|
if (!this.isCollaborating) {
|
|
468
438
|
setSlideOnRemove(interval.start);
|
|
469
439
|
setSlideOnRemove(interval.end);
|
|
470
440
|
}
|
|
471
|
-
const serializedInterval =
|
|
472
|
-
start: startPos,
|
|
473
|
-
end: endPos,
|
|
474
|
-
intervalType: IntervalType.SlideOnRemove,
|
|
475
|
-
properties: { ...interval.properties },
|
|
476
|
-
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
477
|
-
stickiness,
|
|
478
|
-
startSide,
|
|
479
|
-
endSide,
|
|
480
|
-
};
|
|
441
|
+
const serializedInterval = interval.serialize();
|
|
481
442
|
const localSeq = this.getNextLocalSeq();
|
|
482
443
|
if (this.isCollaborating) {
|
|
483
444
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -554,29 +515,21 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
554
515
|
let deltaProps;
|
|
555
516
|
let newInterval;
|
|
556
517
|
if (props !== undefined) {
|
|
557
|
-
interval.
|
|
558
|
-
deltaProps = interval.propertyManager.handleProperties({ props }, interval, this.isCollaborating ? UnassignedSequenceNumber : UniversalSequenceNumber, UniversalSequenceNumber, true);
|
|
518
|
+
deltaProps = interval.changeProperties(props);
|
|
559
519
|
}
|
|
560
|
-
|
|
520
|
+
const changeEndpoints = start !== undefined && end !== undefined;
|
|
521
|
+
if (changeEndpoints) {
|
|
561
522
|
newInterval = this.localCollection.changeInterval(interval, start, end);
|
|
562
523
|
if (!this.isCollaborating && newInterval !== undefined) {
|
|
563
524
|
setSlideOnRemove(newInterval.start);
|
|
564
525
|
setSlideOnRemove(newInterval.end);
|
|
565
526
|
}
|
|
566
527
|
}
|
|
567
|
-
const serializedInterval = interval.serialize();
|
|
568
|
-
const { startPos, startSide, endPos, endSide } = endpointPosAndSide(start, end);
|
|
569
|
-
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
570
|
-
serializedInterval.start = startPos;
|
|
571
|
-
serializedInterval.end = endPos;
|
|
572
|
-
serializedInterval.startSide = startSide;
|
|
573
|
-
serializedInterval.endSide = endSide;
|
|
574
|
-
serializedInterval.stickiness = stickiness;
|
|
575
528
|
// Emit a property bag containing the ID and the other (if any) properties changed
|
|
576
|
-
serializedInterval
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
};
|
|
529
|
+
const serializedInterval = (newInterval ?? interval).serializeDelta({
|
|
530
|
+
props,
|
|
531
|
+
includeEndpoints: changeEndpoints,
|
|
532
|
+
});
|
|
580
533
|
const localSeq = this.getNextLocalSeq();
|
|
581
534
|
if (this.isCollaborating) {
|
|
582
535
|
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
@@ -626,7 +579,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
626
579
|
}
|
|
627
580
|
removePendingChange(serializedInterval) {
|
|
628
581
|
// Change ops always have an ID.
|
|
629
|
-
const id = serializedInterval
|
|
582
|
+
const { id } = getSerializedProperties(serializedInterval);
|
|
630
583
|
if (serializedInterval.start !== undefined) {
|
|
631
584
|
this.removePendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
632
585
|
}
|
|
@@ -668,7 +621,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
668
621
|
// Note that the ID is in the property bag only to allow us to find the interval.
|
|
669
622
|
// This API cannot change the ID, and writing to the ID property will result in an exception. So we
|
|
670
623
|
// strip it out of the properties here.
|
|
671
|
-
const {
|
|
624
|
+
const { id, properties } = getSerializedProperties(serializedInterval);
|
|
672
625
|
assert(id !== undefined, 0x3fe /* id must exist on the interval */);
|
|
673
626
|
const interval = this.getIntervalById(id);
|
|
674
627
|
if (!interval) {
|
|
@@ -676,11 +629,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
676
629
|
return;
|
|
677
630
|
}
|
|
678
631
|
if (local) {
|
|
679
|
-
interval.
|
|
680
|
-
// Let the propertyManager prune its pending change-properties set.
|
|
681
|
-
interval.propertyManager.ack(op.sequenceNumber, op.minimumSequenceNumber, {
|
|
682
|
-
props: newProps,
|
|
683
|
-
});
|
|
632
|
+
interval.ackPropertiesChange(properties, op);
|
|
684
633
|
this.ackInterval(interval, op);
|
|
685
634
|
}
|
|
686
635
|
else {
|
|
@@ -702,15 +651,14 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
702
651
|
newInterval =
|
|
703
652
|
this.localCollection.changeInterval(interval, toOptionalSequencePlace(start, serializedInterval.startSide ?? Side.Before), toOptionalSequencePlace(end, serializedInterval.endSide ?? Side.Before), op) ?? interval;
|
|
704
653
|
}
|
|
705
|
-
|
|
706
|
-
const deltaProps = newInterval.propertyManager.handleProperties({ props: newProps }, newInterval, op.sequenceNumber, op.minimumSequenceNumber, true);
|
|
654
|
+
const deltaProps = newInterval.changeProperties(properties, op);
|
|
707
655
|
if (this.onDeserialize) {
|
|
708
656
|
this.onDeserialize(newInterval);
|
|
709
657
|
}
|
|
710
658
|
if (newInterval !== interval) {
|
|
711
659
|
this.emitChange(newInterval, interval, local, false, op);
|
|
712
660
|
}
|
|
713
|
-
const changedProperties = Object.keys(
|
|
661
|
+
const changedProperties = Object.keys(properties).length > 0;
|
|
714
662
|
if (changedProperties) {
|
|
715
663
|
this.emit("propertyChanged", interval, deltaProps, local, op);
|
|
716
664
|
this.emit("changed", interval, deltaProps, undefined, local, false);
|
|
@@ -747,9 +695,9 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
747
695
|
throw new LoggingError("attachSequence must be called");
|
|
748
696
|
}
|
|
749
697
|
const { intervalType, properties, stickiness, startSide, endSide } = serializedInterval;
|
|
698
|
+
const { id } = getSerializedProperties(serializedInterval);
|
|
750
699
|
const { start: startRebased, end: endRebased } = this.localSeqToRebasedInterval.get(localSeq) ?? this.computeRebasedPositions(localSeq);
|
|
751
|
-
const
|
|
752
|
-
const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(intervalId);
|
|
700
|
+
const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(id);
|
|
753
701
|
const rebased = {
|
|
754
702
|
start: startRebased,
|
|
755
703
|
end: endRebased,
|
|
@@ -762,9 +710,9 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
762
710
|
};
|
|
763
711
|
if (opName === "change" &&
|
|
764
712
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- ?? is not logically equivalent when .hasPendingChangeStart returns false.
|
|
765
|
-
(this.hasPendingChangeStart(
|
|
713
|
+
(this.hasPendingChangeStart(id) || this.hasPendingChangeEnd(id))) {
|
|
766
714
|
this.removePendingChange(serializedInterval);
|
|
767
|
-
this.addPendingChange(
|
|
715
|
+
this.addPendingChange(id, rebased);
|
|
768
716
|
}
|
|
769
717
|
// if the interval slid off the string, rebase the op to be a noop and delete the interval.
|
|
770
718
|
if (!this.options.mergeTreeReferencesCanSlideToEndpoint &&
|
|
@@ -792,7 +740,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
792
740
|
if (segoff.segment?.localRefs?.has(lref) !== true) {
|
|
793
741
|
return undefined;
|
|
794
742
|
}
|
|
795
|
-
const newSegoff = getSlideToSegoff(segoff, slidingPreference, this.options.mergeTreeReferencesCanSlideToEndpoint);
|
|
743
|
+
const newSegoff = getSlideToSegoff(segoff, slidingPreference, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint);
|
|
796
744
|
const value = segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset
|
|
797
745
|
? undefined
|
|
798
746
|
: newSegoff;
|
|
@@ -805,7 +753,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
805
753
|
}
|
|
806
754
|
const newStart = this.getSlideToSegment(interval.start, startReferenceSlidingPreference(interval.stickiness));
|
|
807
755
|
const newEnd = this.getSlideToSegment(interval.end, endReferenceSlidingPreference(interval.stickiness));
|
|
808
|
-
const id = interval.
|
|
756
|
+
const id = interval.getIntervalId();
|
|
809
757
|
const hasPendingStartChange = this.hasPendingChangeStart(id);
|
|
810
758
|
const hasPendingEndChange = this.hasPendingChangeEnd(id);
|
|
811
759
|
if (!hasPendingStartChange) {
|
|
@@ -858,10 +806,10 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
858
806
|
}
|
|
859
807
|
}
|
|
860
808
|
ackAdd(serializedInterval, local, op, localOpMetadata) {
|
|
809
|
+
const { id, properties } = getSerializedProperties(serializedInterval);
|
|
861
810
|
if (local) {
|
|
862
811
|
assert(localOpMetadata !== undefined, 0x553 /* op metadata should be defined for local op */);
|
|
863
812
|
this.localSeqToSerializedInterval.delete(localOpMetadata.localSeq);
|
|
864
|
-
const id = serializedInterval.properties?.[reservedIntervalIdKey];
|
|
865
813
|
const localInterval = this.getIntervalById(id);
|
|
866
814
|
if (localInterval) {
|
|
867
815
|
this.ackInterval(localInterval, op);
|
|
@@ -871,8 +819,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
871
819
|
if (!this.localCollection) {
|
|
872
820
|
throw new LoggingError("attachSequence must be called");
|
|
873
821
|
}
|
|
874
|
-
this.localCollection.
|
|
875
|
-
const interval = this.localCollection.addInterval(toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? Side.Before), toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? Side.Before), serializedInterval.intervalType, serializedInterval.properties, op);
|
|
822
|
+
const interval = this.localCollection.addInterval(id, toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? Side.Before), toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? Side.Before), properties, op);
|
|
876
823
|
if (interval) {
|
|
877
824
|
if (this.onDeserialize) {
|
|
878
825
|
this.onDeserialize(interval);
|
|
@@ -891,7 +838,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
891
838
|
if (!this.localCollection) {
|
|
892
839
|
throw new LoggingError("attach must be called prior to deleting intervals");
|
|
893
840
|
}
|
|
894
|
-
const id =
|
|
841
|
+
const { id } = getSerializedProperties(serializedInterval);
|
|
895
842
|
const interval = this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
896
843
|
if (interval) {
|
|
897
844
|
this.deleteExistingInterval(interval, local, op);
|