@fluidframework/sequence 2.0.0-internal.2.3.1 → 2.0.0-internal.3.0.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/dist/intervalCollection.d.ts +7 -3
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalCollection.js +96 -45
- package/dist/intervalCollection.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/sequence.d.ts.map +1 -1
- package/dist/sequence.js +4 -33
- package/dist/sequence.js.map +1 -1
- package/lib/intervalCollection.d.ts +7 -3
- package/lib/intervalCollection.d.ts.map +1 -1
- package/lib/intervalCollection.js +96 -45
- package/lib/intervalCollection.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/sequence.d.ts.map +1 -1
- package/lib/sequence.js +4 -33
- package/lib/sequence.js.map +1 -1
- package/package.json +21 -20
- package/src/intervalCollection.ts +116 -51
- package/src/packageVersion.ts +1 -1
- package/src/sequence.ts +5 -33
|
@@ -423,13 +423,17 @@ export class SequenceInterval {
|
|
|
423
423
|
}
|
|
424
424
|
}
|
|
425
425
|
}
|
|
426
|
-
function createPositionReferenceFromSegoff(client, segoff, refType, op) {
|
|
426
|
+
function createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq) {
|
|
427
427
|
if (segoff.segment) {
|
|
428
428
|
const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined);
|
|
429
429
|
return ref;
|
|
430
430
|
}
|
|
431
|
-
|
|
432
|
-
|
|
431
|
+
// Creating references on detached segments is allowed for:
|
|
432
|
+
// - Transient segments
|
|
433
|
+
// - References coming from a remote client (location may have been concurrently removed)
|
|
434
|
+
// - References being rebased to a new sequence number
|
|
435
|
+
// (segment they originally referred to may have been removed with no suitable replacement)
|
|
436
|
+
if (!op && !localSeq && !refTypeIncludesFlag(refType, ReferenceType.Transient)) {
|
|
433
437
|
throw new UsageError("Non-transient references need segment");
|
|
434
438
|
}
|
|
435
439
|
return createDetachedLocalReferencePosition(refType);
|
|
@@ -438,14 +442,14 @@ function createPositionReference(client, pos, refType, op, fromSnapshot, localSe
|
|
|
438
442
|
let segoff;
|
|
439
443
|
if (op) {
|
|
440
444
|
assert((refType & ReferenceType.SlideOnRemove) !== 0, 0x2f5 /* op create references must be SlideOnRemove */);
|
|
441
|
-
segoff = client.getContainingSegment(pos, op);
|
|
445
|
+
segoff = client.getContainingSegment(pos, { referenceSequenceNumber: op.referenceSequenceNumber, clientId: op.clientId });
|
|
442
446
|
segoff = client.getSlideToSegment(segoff);
|
|
443
447
|
}
|
|
444
448
|
else {
|
|
445
449
|
assert((refType & ReferenceType.SlideOnRemove) === 0 || !!fromSnapshot, 0x2f6 /* SlideOnRemove references must be op created */);
|
|
446
450
|
segoff = client.getContainingSegment(pos, undefined, localSeq);
|
|
447
451
|
}
|
|
448
|
-
return createPositionReferenceFromSegoff(client, segoff, refType, op);
|
|
452
|
+
return createPositionReferenceFromSegoff(client, segoff, refType, op, localSeq);
|
|
449
453
|
}
|
|
450
454
|
export function createSequenceInterval(label, start, end, client, intervalType, op, fromSnapshot) {
|
|
451
455
|
let beginRefType = ReferenceType.RangeBegin;
|
|
@@ -740,18 +744,25 @@ export class LocalIntervalCollection {
|
|
|
740
744
|
};
|
|
741
745
|
if (interval instanceof SequenceInterval) {
|
|
742
746
|
let previousInterval;
|
|
747
|
+
let pendingChanges = 0;
|
|
743
748
|
interval.addPositionChangeListeners(() => {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
+
pendingChanges++;
|
|
750
|
+
// Note: both start and end can change and invoke beforeSlide on each endpoint before afterSlide.
|
|
751
|
+
if (!previousInterval) {
|
|
752
|
+
previousInterval = interval.clone();
|
|
753
|
+
previousInterval.start = cloneRef(previousInterval.start);
|
|
754
|
+
previousInterval.end = cloneRef(previousInterval.end);
|
|
755
|
+
this.removeIntervalFromIndex(interval);
|
|
756
|
+
}
|
|
749
757
|
}, () => {
|
|
750
758
|
var _a;
|
|
751
759
|
assert(previousInterval !== undefined, 0x3fa /* Invalid interleaving of before/after slide */);
|
|
752
|
-
|
|
753
|
-
(
|
|
754
|
-
|
|
760
|
+
pendingChanges--;
|
|
761
|
+
if (pendingChanges === 0) {
|
|
762
|
+
this.addIntervalToIndex(interval);
|
|
763
|
+
(_a = this.onPositionChange) === null || _a === void 0 ? void 0 : _a.call(this, interval, previousInterval);
|
|
764
|
+
previousInterval = undefined;
|
|
765
|
+
}
|
|
755
766
|
});
|
|
756
767
|
}
|
|
757
768
|
}
|
|
@@ -835,14 +846,14 @@ export function makeOpsMap() {
|
|
|
835
846
|
return new Map([[
|
|
836
847
|
"add",
|
|
837
848
|
{
|
|
838
|
-
process: (collection, params, local, op) => {
|
|
849
|
+
process: (collection, params, local, op, localOpMetadata) => {
|
|
839
850
|
// if params is undefined, the interval was deleted during
|
|
840
851
|
// rebasing
|
|
841
852
|
if (!params) {
|
|
842
853
|
return;
|
|
843
854
|
}
|
|
844
855
|
assert(op !== undefined, 0x3fb /* op should exist here */);
|
|
845
|
-
collection.ackAdd(params, local, op);
|
|
856
|
+
collection.ackAdd(params, local, op, localOpMetadata);
|
|
846
857
|
},
|
|
847
858
|
rebase,
|
|
848
859
|
},
|
|
@@ -863,14 +874,14 @@ export function makeOpsMap() {
|
|
|
863
874
|
[
|
|
864
875
|
"change",
|
|
865
876
|
{
|
|
866
|
-
process: (collection, params, local, op) => {
|
|
877
|
+
process: (collection, params, local, op, localOpMetadata) => {
|
|
867
878
|
// if params is undefined, the interval was deleted during
|
|
868
879
|
// rebasing
|
|
869
880
|
if (!params) {
|
|
870
881
|
return;
|
|
871
882
|
}
|
|
872
883
|
assert(op !== undefined, 0x3fd /* op should exist here */);
|
|
873
|
-
collection.ackChange(params, local, op);
|
|
884
|
+
collection.ackChange(params, local, op, localOpMetadata);
|
|
874
885
|
},
|
|
875
886
|
rebase,
|
|
876
887
|
},
|
|
@@ -909,6 +920,8 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
909
920
|
this.helpers = helpers;
|
|
910
921
|
this.requiresClient = requiresClient;
|
|
911
922
|
this.emitter = emitter;
|
|
923
|
+
this.localSeqToSerializedInterval = new Map();
|
|
924
|
+
this.localSeqToRebasedInterval = new Map();
|
|
912
925
|
this.pendingChangesStart = new Map();
|
|
913
926
|
this.pendingChangesEnd = new Map();
|
|
914
927
|
this.savedSerializedIntervals = Array.isArray(serializedIntervals)
|
|
@@ -918,6 +931,37 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
918
931
|
get attached() {
|
|
919
932
|
return !!this.localCollection;
|
|
920
933
|
}
|
|
934
|
+
rebasePositionWithSegmentSlide(pos, seqNumberFrom, localSeq) {
|
|
935
|
+
var _a;
|
|
936
|
+
if (!this.client) {
|
|
937
|
+
throw new LoggingError("mergeTree client must exist");
|
|
938
|
+
}
|
|
939
|
+
const { clientId } = this.client.getCollabWindow();
|
|
940
|
+
const { segment, offset } = this.client.getContainingSegment(pos, { referenceSequenceNumber: seqNumberFrom, clientId: this.client.getLongClientId(clientId) }, localSeq);
|
|
941
|
+
// if segment is undefined, it slid off the string
|
|
942
|
+
assert(segment !== undefined, 0x54e /* No segment found */);
|
|
943
|
+
const segoff = (_a = this.client.getSlideToSegment({ segment, offset })) !== null && _a !== void 0 ? _a : segment;
|
|
944
|
+
// case happens when rebasing op, but concurrently entire string has been deleted
|
|
945
|
+
if (segoff.segment === undefined || segoff.offset === undefined) {
|
|
946
|
+
return DetachedReferencePosition;
|
|
947
|
+
}
|
|
948
|
+
assert(offset !== undefined && 0 <= offset && offset < segment.cachedLength, 0x54f /* Invalid offset */);
|
|
949
|
+
return this.client.findReconnectionPosition(segoff.segment, localSeq) + segoff.offset;
|
|
950
|
+
}
|
|
951
|
+
computeRebasedPositions(localSeq) {
|
|
952
|
+
assert(this.client !== undefined, 0x550 /* Client should be defined when computing rebased position */);
|
|
953
|
+
const original = this.localSeqToSerializedInterval.get(localSeq);
|
|
954
|
+
assert(original !== undefined, 0x551 /* Failed to store pending serialized interval info for this localSeq. */);
|
|
955
|
+
const rebased = Object.assign({}, original);
|
|
956
|
+
const { start, end, sequenceNumber } = original;
|
|
957
|
+
if (start !== undefined) {
|
|
958
|
+
rebased.start = this.rebasePositionWithSegmentSlide(start, sequenceNumber, localSeq);
|
|
959
|
+
}
|
|
960
|
+
if (end !== undefined) {
|
|
961
|
+
rebased.end = this.rebasePositionWithSegmentSlide(end, sequenceNumber, localSeq);
|
|
962
|
+
}
|
|
963
|
+
return rebased;
|
|
964
|
+
}
|
|
921
965
|
/** @internal */
|
|
922
966
|
attachGraph(client, label) {
|
|
923
967
|
if (this.attached) {
|
|
@@ -928,6 +972,13 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
928
972
|
}
|
|
929
973
|
// Instantiate the local interval collection based on the saved intervals
|
|
930
974
|
this.client = client;
|
|
975
|
+
if (client) {
|
|
976
|
+
client.on("normalize", () => {
|
|
977
|
+
for (const localSeq of this.localSeqToSerializedInterval.keys()) {
|
|
978
|
+
this.localSeqToRebasedInterval.set(localSeq, this.computeRebasedPositions(localSeq));
|
|
979
|
+
}
|
|
980
|
+
});
|
|
981
|
+
}
|
|
931
982
|
this.localCollection = new LocalIntervalCollection(client, label, this.helpers, (interval, previousInterval) => this.emitChange(interval, previousInterval, true));
|
|
932
983
|
if (this.savedSerializedIntervals) {
|
|
933
984
|
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
@@ -1005,8 +1056,10 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1005
1056
|
sequenceNumber: (_b = (_a = this.client) === null || _a === void 0 ? void 0 : _a.getCurrentSeq()) !== null && _b !== void 0 ? _b : 0,
|
|
1006
1057
|
start,
|
|
1007
1058
|
};
|
|
1059
|
+
const localSeq = this.getNextLocalSeq();
|
|
1060
|
+
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
1008
1061
|
// Local ops get submitted to the server. Remote ops have the deserializer run.
|
|
1009
|
-
this.emitter.emit("add", undefined, serializedInterval, { localSeq
|
|
1062
|
+
this.emitter.emit("add", undefined, serializedInterval, { localSeq });
|
|
1010
1063
|
}
|
|
1011
1064
|
this.emit("addInterval", interval, true, undefined);
|
|
1012
1065
|
return interval;
|
|
@@ -1072,7 +1125,9 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1072
1125
|
serializedInterval.end = undefined;
|
|
1073
1126
|
serializedInterval.properties = props;
|
|
1074
1127
|
serializedInterval.properties[reservedIntervalIdKey] = interval.getIntervalId();
|
|
1075
|
-
|
|
1128
|
+
const localSeq = this.getNextLocalSeq();
|
|
1129
|
+
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
1130
|
+
this.emitter.emit("change", undefined, serializedInterval, { localSeq });
|
|
1076
1131
|
this.emit("propertyChanged", interval, deltaProps, true, undefined);
|
|
1077
1132
|
}
|
|
1078
1133
|
}
|
|
@@ -1105,7 +1160,9 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1105
1160
|
{
|
|
1106
1161
|
[reservedIntervalIdKey]: interval.getIntervalId(),
|
|
1107
1162
|
};
|
|
1108
|
-
|
|
1163
|
+
const localSeq = this.getNextLocalSeq();
|
|
1164
|
+
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
1165
|
+
this.emitter.emit("change", undefined, serializedInterval, { localSeq });
|
|
1109
1166
|
this.addPendingChange(id, serializedInterval);
|
|
1110
1167
|
this.emitChange(newInterval, interval, true);
|
|
1111
1168
|
return newInterval;
|
|
@@ -1162,12 +1219,14 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1162
1219
|
return entries && entries.length !== 0;
|
|
1163
1220
|
}
|
|
1164
1221
|
/** @internal */
|
|
1165
|
-
ackChange(serializedInterval, local, op) {
|
|
1222
|
+
ackChange(serializedInterval, local, op, localOpMetadata) {
|
|
1166
1223
|
var _a, _b, _c, _d;
|
|
1167
1224
|
if (!this.localCollection) {
|
|
1168
1225
|
throw new LoggingError("Attach must be called before accessing intervals");
|
|
1169
1226
|
}
|
|
1170
1227
|
if (local) {
|
|
1228
|
+
assert(localOpMetadata !== undefined, 0x552 /* op metadata should be defined for local op */);
|
|
1229
|
+
this.localSeqToSerializedInterval.delete(localOpMetadata === null || localOpMetadata === void 0 ? void 0 : localOpMetadata.localSeq);
|
|
1171
1230
|
// This is an ack from the server. Remove the pending change.
|
|
1172
1231
|
this.removePendingChange(serializedInterval);
|
|
1173
1232
|
}
|
|
@@ -1247,7 +1306,7 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1247
1306
|
* @internal
|
|
1248
1307
|
*/
|
|
1249
1308
|
rebaseLocalInterval(opName, serializedInterval, localSeq) {
|
|
1250
|
-
var _a, _b, _c, _d, _e, _f
|
|
1309
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1251
1310
|
if (!this.client) {
|
|
1252
1311
|
// If there's no associated mergeTree client, the originally submitted op is still correct.
|
|
1253
1312
|
return serializedInterval;
|
|
@@ -1255,44 +1314,34 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1255
1314
|
if (!this.attached) {
|
|
1256
1315
|
throw new LoggingError("attachSequence must be called");
|
|
1257
1316
|
}
|
|
1258
|
-
const {
|
|
1259
|
-
const startRebased =
|
|
1260
|
-
this.client.rebasePosition(start, sequenceNumber, localSeq);
|
|
1261
|
-
const endRebased = end === undefined ? undefined :
|
|
1262
|
-
this.client.rebasePosition(end, sequenceNumber, localSeq);
|
|
1317
|
+
const { intervalType, properties } = serializedInterval;
|
|
1318
|
+
const { start: startRebased, end: endRebased } = (_a = this.localSeqToRebasedInterval.get(localSeq)) !== null && _a !== void 0 ? _a : this.computeRebasedPositions(localSeq);
|
|
1263
1319
|
const intervalId = properties === null || properties === void 0 ? void 0 : properties[reservedIntervalIdKey];
|
|
1264
|
-
const localInterval = (
|
|
1320
|
+
const localInterval = (_b = this.localCollection) === null || _b === void 0 ? void 0 : _b.getIntervalById(intervalId);
|
|
1265
1321
|
const rebased = {
|
|
1266
1322
|
start: startRebased,
|
|
1267
1323
|
end: endRebased,
|
|
1268
1324
|
intervalType,
|
|
1269
|
-
sequenceNumber: (
|
|
1325
|
+
sequenceNumber: (_d = (_c = this.client) === null || _c === void 0 ? void 0 : _c.getCurrentSeq()) !== null && _d !== void 0 ? _d : 0,
|
|
1270
1326
|
properties,
|
|
1271
1327
|
};
|
|
1272
1328
|
if (opName === "change" && (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
|
|
1273
1329
|
this.removePendingChange(serializedInterval);
|
|
1274
1330
|
this.addPendingChange(intervalId, rebased);
|
|
1275
1331
|
}
|
|
1276
|
-
// if the interval slid off the string, rebase the op to be a noop and
|
|
1277
|
-
// delete the interval
|
|
1332
|
+
// if the interval slid off the string, rebase the op to be a noop and delete the interval.
|
|
1278
1333
|
if (startRebased === DetachedReferencePosition || endRebased === DetachedReferencePosition) {
|
|
1279
1334
|
if (localInterval) {
|
|
1280
|
-
(
|
|
1335
|
+
(_e = this.localCollection) === null || _e === void 0 ? void 0 : _e.removeExistingInterval(localInterval);
|
|
1281
1336
|
}
|
|
1282
1337
|
return undefined;
|
|
1283
1338
|
}
|
|
1284
|
-
if (
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
const endSegment = this.getSlideToSegment(localInterval.end);
|
|
1291
|
-
// we need to slide because the reference has been removed
|
|
1292
|
-
if (startSegment || endSegment) {
|
|
1293
|
-
const newStart = startSegment && this.client.getPosition(startSegment.segment, localSeq) + ((_e = startSegment.offset) !== null && _e !== void 0 ? _e : 0);
|
|
1294
|
-
const newEnd = endSegment && this.client.getPosition(endSegment.segment, localSeq) + ((_f = endSegment.offset) !== null && _f !== void 0 ? _f : 0);
|
|
1295
|
-
(_g = this.localCollection) === null || _g === void 0 ? void 0 : _g.changeInterval(localInterval, newStart, newEnd, undefined, localSeq);
|
|
1339
|
+
if (localInterval !== undefined) {
|
|
1340
|
+
// we know we must be using `SequenceInterval` because `this.client` exists
|
|
1341
|
+
assert(localInterval instanceof SequenceInterval, 0x3a0 /* localInterval must be `SequenceInterval` when used with client */);
|
|
1342
|
+
// The rebased op may place this interval's endpoints on different segments. Calling `changeInterval` here
|
|
1343
|
+
// updates the local client's state to be consistent with the emitted op.
|
|
1344
|
+
(_f = this.localCollection) === null || _f === void 0 ? void 0 : _f.changeInterval(localInterval, startRebased, endRebased, undefined, localSeq);
|
|
1296
1345
|
}
|
|
1297
1346
|
return rebased;
|
|
1298
1347
|
}
|
|
@@ -1380,9 +1429,11 @@ export class IntervalCollection extends TypedEventEmitter {
|
|
|
1380
1429
|
}
|
|
1381
1430
|
}
|
|
1382
1431
|
/** @internal */
|
|
1383
|
-
ackAdd(serializedInterval, local, op) {
|
|
1432
|
+
ackAdd(serializedInterval, local, op, localOpMetadata) {
|
|
1384
1433
|
var _a;
|
|
1385
1434
|
if (local) {
|
|
1435
|
+
assert(localOpMetadata !== undefined, 0x553 /* op metadata should be defined for local op */);
|
|
1436
|
+
this.localSeqToSerializedInterval.delete(localOpMetadata.localSeq);
|
|
1386
1437
|
const id = (_a = serializedInterval.properties) === null || _a === void 0 ? void 0 : _a[reservedIntervalIdKey];
|
|
1387
1438
|
const localInterval = this.getIntervalById(id);
|
|
1388
1439
|
if (localInterval) {
|