@fluidframework/sequence 1.2.7 → 2.0.0-dev.1.3.0.96595

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.
Files changed (73) hide show
  1. package/.mocharc.js +12 -0
  2. package/README.md +19 -18
  3. package/dist/index.d.ts +2 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +3 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/intervalCollection.d.ts +39 -17
  8. package/dist/intervalCollection.d.ts.map +1 -1
  9. package/dist/intervalCollection.js +108 -67
  10. package/dist/intervalCollection.js.map +1 -1
  11. package/dist/packageVersion.d.ts +1 -1
  12. package/dist/packageVersion.d.ts.map +1 -1
  13. package/dist/packageVersion.js +1 -1
  14. package/dist/packageVersion.js.map +1 -1
  15. package/dist/sequence.d.ts +13 -22
  16. package/dist/sequence.d.ts.map +1 -1
  17. package/dist/sequence.js +11 -32
  18. package/dist/sequence.js.map +1 -1
  19. package/dist/sequenceDeltaEvent.d.ts +0 -6
  20. package/dist/sequenceDeltaEvent.d.ts.map +1 -1
  21. package/dist/sequenceDeltaEvent.js +0 -1
  22. package/dist/sequenceDeltaEvent.js.map +1 -1
  23. package/dist/sharedIntervalCollection.d.ts +5 -5
  24. package/dist/sharedIntervalCollection.js +5 -5
  25. package/dist/sharedIntervalCollection.js.map +1 -1
  26. package/dist/sharedString.d.ts +30 -1
  27. package/dist/sharedString.d.ts.map +1 -1
  28. package/dist/sharedString.js +40 -5
  29. package/dist/sharedString.js.map +1 -1
  30. package/dist/sparsematrix.d.ts +28 -15
  31. package/dist/sparsematrix.d.ts.map +1 -1
  32. package/dist/sparsematrix.js +24 -13
  33. package/dist/sparsematrix.js.map +1 -1
  34. package/lib/index.d.ts +2 -2
  35. package/lib/index.d.ts.map +1 -1
  36. package/lib/index.js +2 -2
  37. package/lib/index.js.map +1 -1
  38. package/lib/intervalCollection.d.ts +39 -17
  39. package/lib/intervalCollection.d.ts.map +1 -1
  40. package/lib/intervalCollection.js +107 -67
  41. package/lib/intervalCollection.js.map +1 -1
  42. package/lib/packageVersion.d.ts +1 -1
  43. package/lib/packageVersion.d.ts.map +1 -1
  44. package/lib/packageVersion.js +1 -1
  45. package/lib/packageVersion.js.map +1 -1
  46. package/lib/sequence.d.ts +13 -22
  47. package/lib/sequence.d.ts.map +1 -1
  48. package/lib/sequence.js +12 -33
  49. package/lib/sequence.js.map +1 -1
  50. package/lib/sequenceDeltaEvent.d.ts +0 -6
  51. package/lib/sequenceDeltaEvent.d.ts.map +1 -1
  52. package/lib/sequenceDeltaEvent.js +0 -1
  53. package/lib/sequenceDeltaEvent.js.map +1 -1
  54. package/lib/sharedIntervalCollection.d.ts +5 -5
  55. package/lib/sharedIntervalCollection.js +5 -5
  56. package/lib/sharedIntervalCollection.js.map +1 -1
  57. package/lib/sharedString.d.ts +30 -1
  58. package/lib/sharedString.d.ts.map +1 -1
  59. package/lib/sharedString.js +38 -4
  60. package/lib/sharedString.js.map +1 -1
  61. package/lib/sparsematrix.d.ts +28 -15
  62. package/lib/sparsematrix.d.ts.map +1 -1
  63. package/lib/sparsematrix.js +24 -13
  64. package/lib/sparsematrix.js.map +1 -1
  65. package/package.json +70 -25
  66. package/src/index.ts +3 -1
  67. package/src/intervalCollection.ts +169 -85
  68. package/src/packageVersion.ts +1 -1
  69. package/src/sequence.ts +12 -41
  70. package/src/sequenceDeltaEvent.ts +0 -7
  71. package/src/sharedIntervalCollection.ts +5 -5
  72. package/src/sharedString.ts +44 -6
  73. package/src/sparsematrix.ts +48 -35
@@ -11,6 +11,7 @@ import { UsageError } from "@fluidframework/container-utils";
11
11
  import {
12
12
  addProperties,
13
13
  Client,
14
+ compareReferencePositions,
14
15
  ConflictAction,
15
16
  createMap,
16
17
  ICombiningOp,
@@ -19,15 +20,19 @@ import {
19
20
  IntervalNode,
20
21
  IntervalTree,
21
22
  ISegment,
22
- LocalReference,
23
23
  MergeTreeDeltaType,
24
+ minReferencePosition,
24
25
  PropertiesManager,
25
26
  PropertySet,
26
27
  RedBlackTree,
28
+ LocalReferencePosition,
27
29
  ReferenceType,
28
30
  refTypeIncludesFlag,
29
31
  reservedRangeLabelsKey,
30
32
  UnassignedSequenceNumber,
33
+ maxReferencePosition,
34
+ createDetachedLocalReferencePosition,
35
+ DetachedReferencePosition,
31
36
  } from "@fluidframework/merge-tree";
32
37
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
33
38
  import { LoggingError } from "@fluidframework/telemetry-utils";
@@ -46,6 +51,7 @@ const reservedIntervalIdKey = "intervalId";
46
51
  export enum IntervalType {
47
52
  Simple = 0x0,
48
53
  Nest = 0x1,
54
+
49
55
  /**
50
56
  * SlideOnRemove indicates that the ends of the interval will slide if the segment
51
57
  * they reference is removed and acked.
@@ -53,9 +59,10 @@ export enum IntervalType {
53
59
  * SlideOnRemove is the default interval behavior and does not need to be specified.
54
60
  */
55
61
  SlideOnRemove = 0x2, // SlideOnRemove is default behavior - all intervals are SlideOnRemove
62
+
56
63
  /**
57
- * @internal
58
64
  * A temporary interval, used internally
65
+ * @internal
59
66
  */
60
67
  Transient = 0x4,
61
68
  }
@@ -95,7 +102,7 @@ function decompressInterval(interval: CompressedSerializedInterval, label?: stri
95
102
  end: interval[1],
96
103
  sequenceNumber: interval[2],
97
104
  intervalType: interval[3],
98
- properties: { ...interval[4], [reservedRangeLabelsKey]: label },
105
+ properties: { ...interval[4], [reservedRangeLabelsKey]: [label] },
99
106
  };
100
107
  }
101
108
 
@@ -187,11 +194,7 @@ export class Interval implements ISerializableInterval {
187
194
  }
188
195
 
189
196
  public serialize(client: Client): ISerializedInterval {
190
- let seq = 0;
191
- if (client) {
192
- seq = client.getCurrentSeq();
193
- }
194
-
197
+ const seq = client?.getCurrentSeq() ?? 0;
195
198
  const serializedInterval: ISerializedInterval = {
196
199
  end: this.end,
197
200
  intervalType: 0,
@@ -295,8 +298,9 @@ export class SequenceInterval implements ISerializableInterval {
295
298
  public propertyManager: PropertiesManager;
296
299
 
297
300
  constructor(
298
- public start: LocalReference,
299
- public end: LocalReference,
301
+ private readonly client: Client,
302
+ public start: LocalReferencePosition,
303
+ public end: LocalReferencePosition,
300
304
  public intervalType: IntervalType,
301
305
  props?: PropertySet,
302
306
  ) {
@@ -311,8 +315,8 @@ export class SequenceInterval implements ISerializableInterval {
311
315
  private callbacks?: Record<"beforePositionChange" | "afterPositionChange", () => void>;
312
316
 
313
317
  /**
314
- * @internal
315
318
  * Subscribes to position change events on this interval if there are no current listeners.
319
+ * @internal
316
320
  */
317
321
  public addPositionChangeListeners(beforePositionChange: () => void, afterPositionChange: () => void): void {
318
322
  if (this.callbacks === undefined) {
@@ -329,8 +333,8 @@ export class SequenceInterval implements ISerializableInterval {
329
333
  }
330
334
 
331
335
  /**
332
- * @internal
333
336
  * Removes the currently subscribed position change listeners.
337
+ * @internal
334
338
  */
335
339
  public removePositionChangeListeners(): void {
336
340
  if (this.callbacks) {
@@ -341,8 +345,8 @@ export class SequenceInterval implements ISerializableInterval {
341
345
  }
342
346
 
343
347
  public serialize(client: Client): ISerializedInterval {
344
- const startPosition = this.start.toPosition();
345
- const endPosition = this.end.toPosition();
348
+ const startPosition = client.localReferencePositionToPosition(this.start);
349
+ const endPosition = client.localReferencePositionToPosition(this.end);
346
350
  const serializedInterval: ISerializedInterval = {
347
351
  end: endPosition,
348
352
  intervalType: this.intervalType,
@@ -358,7 +362,7 @@ export class SequenceInterval implements ISerializableInterval {
358
362
  }
359
363
 
360
364
  public clone() {
361
- return new SequenceInterval(this.start, this.end, this.intervalType, this.properties);
365
+ return new SequenceInterval(this.client, this.start, this.end, this.intervalType, this.properties);
362
366
  }
363
367
 
364
368
  public compare(b: SequenceInterval) {
@@ -384,16 +388,16 @@ export class SequenceInterval implements ISerializableInterval {
384
388
  }
385
389
 
386
390
  public compareStart(b: SequenceInterval) {
387
- return this.start.compare(b.start);
391
+ return compareReferencePositions(this.start, b.start);
388
392
  }
389
393
 
390
394
  public compareEnd(b: SequenceInterval) {
391
- return this.end.compare(b.end);
395
+ return compareReferencePositions(this.end, b.end);
392
396
  }
393
397
 
394
398
  public overlaps(b: SequenceInterval) {
395
- const result = (this.start.compare(b.end) <= 0) &&
396
- (this.end.compare(b.start) >= 0);
399
+ const result = (compareReferencePositions(this.start, b.end) <= 0) &&
400
+ (compareReferencePositions(this.end, b.start) >= 0);
397
401
  return result;
398
402
  }
399
403
 
@@ -406,8 +410,8 @@ export class SequenceInterval implements ISerializableInterval {
406
410
  }
407
411
 
408
412
  public union(b: SequenceInterval) {
409
- return new SequenceInterval(this.start.min(b.start),
410
- this.end.max(b.end), this.intervalType);
413
+ return new SequenceInterval(this.client, minReferencePosition(this.start, b.start),
414
+ maxReferencePosition(this.end, b.end), this.intervalType);
411
415
  }
412
416
 
413
417
  public addProperties(
@@ -421,12 +425,12 @@ export class SequenceInterval implements ISerializableInterval {
421
425
  }
422
426
 
423
427
  public overlapsPos(bstart: number, bend: number) {
424
- const startPos = this.start.toPosition();
425
- const endPos = this.start.toPosition();
428
+ const startPos = this.client.localReferencePositionToPosition(this.start);
429
+ const endPos = this.client.localReferencePositionToPosition(this.end);
426
430
  return (endPos > bstart) && (startPos < bend);
427
431
  }
428
432
 
429
- public modify(label: string, start: number, end: number, op?: ISequencedDocumentMessage) {
433
+ public modify(label: string, start: number, end: number, op?: ISequencedDocumentMessage, localSeq?: number) {
430
434
  const getRefType = (baseType: ReferenceType): ReferenceType => {
431
435
  let refType = baseType;
432
436
  if (op === undefined) {
@@ -438,20 +442,21 @@ export class SequenceInterval implements ISerializableInterval {
438
442
 
439
443
  let startRef = this.start;
440
444
  if (start !== undefined) {
441
- startRef = createPositionReference(this.start.getClient(), start, getRefType(this.start.refType), op);
445
+ startRef = createPositionReference(
446
+ this.client, start, getRefType(this.start.refType), op, undefined, localSeq,
447
+ );
442
448
  startRef.addProperties(this.start.properties);
443
449
  }
444
450
 
445
451
  let endRef = this.end;
446
452
  if (end !== undefined) {
447
- endRef = createPositionReference(this.end.getClient(), end, getRefType(this.end.refType), op);
453
+ endRef = createPositionReference(
454
+ this.client, end, getRefType(this.end.refType), op, undefined, localSeq,
455
+ );
448
456
  endRef.addProperties(this.end.properties);
449
457
  }
450
458
 
451
- startRef.pairedRef = endRef;
452
- endRef.pairedRef = startRef;
453
-
454
- const newInterval = new SequenceInterval(startRef, endRef, this.intervalType);
459
+ const newInterval = new SequenceInterval(this.client, startRef, endRef, this.intervalType);
455
460
  if (this.properties) {
456
461
  newInterval.initializeProperties();
457
462
  this.propertyManager.copyTo(this.properties, newInterval.properties, newInterval.propertyManager);
@@ -473,16 +478,18 @@ function createPositionReferenceFromSegoff(
473
478
  client: Client,
474
479
  segoff: { segment: ISegment | undefined; offset: number | undefined; },
475
480
  refType: ReferenceType,
476
- op?: ISequencedDocumentMessage): LocalReference {
481
+ op?: ISequencedDocumentMessage): LocalReferencePosition {
477
482
  if (segoff.segment) {
478
483
  const ref = client.createLocalReferencePosition(segoff.segment, segoff.offset, refType, undefined);
479
- return ref as LocalReference;
480
- } else {
481
- if (!op && !refTypeIncludesFlag(refType, ReferenceType.Transient)) {
482
- throw new UsageError("Non-transient references need segment");
483
- }
484
- return new LocalReference(client, undefined, 0, refType);
484
+ return ref;
485
+ }
486
+
487
+ if (!op && !refTypeIncludesFlag(refType, ReferenceType.Transient)) {
488
+ // reference to segment that dne locally
489
+ throw new UsageError("Non-transient references need segment");
485
490
  }
491
+
492
+ return createDetachedLocalReferencePosition(refType);
486
493
  }
487
494
 
488
495
  function createPositionReference(
@@ -490,7 +497,9 @@ function createPositionReference(
490
497
  pos: number,
491
498
  refType: ReferenceType,
492
499
  op?: ISequencedDocumentMessage,
493
- fromSnapshot?: boolean): LocalReference {
500
+ fromSnapshot?: boolean,
501
+ localSeq?: number,
502
+ ): LocalReferencePosition {
494
503
  let segoff;
495
504
  if (op) {
496
505
  assert((refType & ReferenceType.SlideOnRemove) !== 0, 0x2f5 /* op create references must be SlideOnRemove */);
@@ -499,7 +508,7 @@ function createPositionReference(
499
508
  } else {
500
509
  assert((refType & ReferenceType.SlideOnRemove) === 0 || fromSnapshot,
501
510
  0x2f6 /* SlideOnRemove references must be op created */);
502
- segoff = client.getContainingSegment(pos);
511
+ segoff = client.getContainingSegment(pos, undefined, localSeq);
503
512
  }
504
513
  return createPositionReferenceFromSegoff(client, segoff, refType, op);
505
514
  }
@@ -536,15 +545,13 @@ function createSequenceInterval(
536
545
 
537
546
  const startLref = createPositionReference(client, start, beginRefType, op, fromSnapshot);
538
547
  const endLref = createPositionReference(client, end, endRefType, op, fromSnapshot);
539
- startLref.pairedRef = endLref;
540
- endLref.pairedRef = startLref;
541
548
  const rangeProp = {
542
549
  [reservedRangeLabelsKey]: [label],
543
550
  };
544
551
  startLref.addProperties(rangeProp);
545
552
  endLref.addProperties(rangeProp);
546
553
 
547
- const ival = new SequenceInterval(startLref, endLref, intervalType, rangeProp);
554
+ const ival = new SequenceInterval(client, startLref, endLref, intervalType, rangeProp);
548
555
  return ival;
549
556
  }
550
557
 
@@ -793,18 +800,23 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
793
800
  if (!interval.properties) {
794
801
  interval.properties = createMap<any>();
795
802
  }
803
+
796
804
  if (props) {
797
805
  interval.addProperties(props);
798
806
  }
799
- if (interval.properties[reservedIntervalIdKey] === undefined) {
800
- // Create a new ID.
801
- interval.properties[reservedIntervalIdKey] = uuid();
802
- }
807
+ interval.properties[reservedIntervalIdKey] ??= uuid();
803
808
  this.add(interval);
804
809
  }
805
810
  return interval;
806
811
  }
807
812
 
813
+ private linkEndpointsToInterval(interval: TInterval): void {
814
+ if (interval instanceof SequenceInterval) {
815
+ interval.start.addProperties({ interval });
816
+ interval.end.addProperties({ interval });
817
+ }
818
+ }
819
+
808
820
  private addIntervalToIndex(interval: TInterval) {
809
821
  const id = interval.getIntervalId();
810
822
  assert(id !== undefined, 0x2c0 /* "ID must be created before adding interval to collection" */);
@@ -820,6 +832,7 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
820
832
  }
821
833
 
822
834
  public add(interval: TInterval) {
835
+ this.linkEndpointsToInterval(interval);
823
836
  this.addIntervalToIndex(interval);
824
837
  this.addIntervalListeners(interval);
825
838
  }
@@ -828,8 +841,14 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
828
841
  return this.intervalIdMap.get(id);
829
842
  }
830
843
 
831
- public changeInterval(interval: TInterval, start: number, end: number, op?: ISequencedDocumentMessage) {
832
- const newInterval = interval.modify(this.label, start, end, op) as TInterval | undefined;
844
+ public changeInterval(
845
+ interval: TInterval,
846
+ start: number,
847
+ end: number,
848
+ op?: ISequencedDocumentMessage,
849
+ localSeq?: number,
850
+ ) {
851
+ const newInterval = interval.modify(this.label, start, end, op, localSeq) as TInterval | undefined;
833
852
  if (newInterval) {
834
853
  this.removeExistingInterval(interval);
835
854
  this.add(newInterval);
@@ -867,7 +886,8 @@ export class LocalIntervalCollection<TInterval extends ISerializableInterval> {
867
886
  }
868
887
  }
869
888
 
870
- const compareSequenceIntervalEnds = (a: SequenceInterval, b: SequenceInterval): number => a.end.compare(b.end);
889
+ const compareSequenceIntervalEnds = (a: SequenceInterval, b: SequenceInterval): number =>
890
+ compareReferencePositions(a.end, b.end);
871
891
 
872
892
  class SequenceIntervalCollectionFactory
873
893
  implements IValueFactory<IntervalCollection<SequenceInterval>> {
@@ -979,6 +999,11 @@ function makeOpsMap<T extends ISerializableInterval>(): Map<string, IValueOperat
979
999
  "add",
980
1000
  {
981
1001
  process: (collection, params, local, op) => {
1002
+ // if params is undefined, the interval was deleted during
1003
+ // rebasing
1004
+ if (!params) {
1005
+ return;
1006
+ }
982
1007
  collection.ackAdd(params, local, op);
983
1008
  },
984
1009
  rebase,
@@ -1000,6 +1025,11 @@ function makeOpsMap<T extends ISerializableInterval>(): Map<string, IValueOperat
1000
1025
  "change",
1001
1026
  {
1002
1027
  process: (collection, params, local, op) => {
1028
+ // if params is undefined, the interval was deleted during
1029
+ // rebasing
1030
+ if (!params) {
1031
+ return;
1032
+ }
1003
1033
  collection.ackChange(params, local, op);
1004
1034
  },
1005
1035
  rebase,
@@ -1062,7 +1092,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
1062
1092
  private savedSerializedIntervals?: ISerializedInterval[];
1063
1093
  private localCollection: LocalIntervalCollection<TInterval>;
1064
1094
  private onDeserialize: DeserializeCallback | undefined;
1065
- private client: Client;
1095
+ private client: Client | undefined;
1066
1096
  private readonly pendingChangesStart: Map<string, ISerializedInterval[]> = new Map<string, ISerializedInterval[]>();
1067
1097
  private readonly pendingChangesEnd: Map<string, ISerializedInterval[]> = new Map<string, ISerializedInterval[]>();
1068
1098
 
@@ -1079,12 +1109,9 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
1079
1109
  ) {
1080
1110
  super();
1081
1111
 
1082
- if (Array.isArray(serializedIntervals)) {
1083
- this.savedSerializedIntervals = serializedIntervals;
1084
- } else {
1085
- this.savedSerializedIntervals =
1086
- serializedIntervals.intervals.map((i) => decompressInterval(i, serializedIntervals.label));
1087
- }
1112
+ this.savedSerializedIntervals = Array.isArray(serializedIntervals)
1113
+ ? serializedIntervals
1114
+ : serializedIntervals.intervals.map((i) => decompressInterval(i, serializedIntervals.label));
1088
1115
  }
1089
1116
 
1090
1117
  public attachGraph(client: Client, label: string) {
@@ -1128,7 +1155,11 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
1128
1155
  * Gets the next local sequence number, modifying this client's collab window in doing so.
1129
1156
  */
1130
1157
  private getNextLocalSeq(): number {
1131
- return ++this.client.getCollabWindow().localSeq;
1158
+ if (this.client) {
1159
+ return ++this.client.getCollabWindow().localSeq;
1160
+ }
1161
+
1162
+ return 0;
1132
1163
  }
1133
1164
 
1134
1165
  public getIntervalById(id: string) {
@@ -1330,11 +1361,6 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
1330
1361
  return entries && entries.length !== 0;
1331
1362
  }
1332
1363
 
1333
- /** @deprecated - use ackChange */
1334
- public changeInterval(serializedInterval: ISerializedInterval, local: boolean, op: ISequencedDocumentMessage) {
1335
- return this.ackChange(serializedInterval, local, op);
1336
- }
1337
-
1338
1364
  /** @internal */
1339
1365
  public ackChange(serializedInterval: ISerializedInterval, local: boolean, op: ISequencedDocumentMessage) {
1340
1366
  if (!this.attached) {
@@ -1415,12 +1441,22 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
1415
1441
  });
1416
1442
  }
1417
1443
 
1418
- /** @internal */
1444
+ /**
1445
+ * Returns new interval after rebasing. If undefined, the interval was
1446
+ * deleted as a result of rebasing. This can occur if the interval applies
1447
+ * to a range that no longer exists, and the interval was unable to slide.
1448
+ *
1449
+ * @internal
1450
+ */
1419
1451
  public rebaseLocalInterval(
1420
1452
  opName: string,
1421
1453
  serializedInterval: ISerializedInterval,
1422
1454
  localSeq: number,
1423
- ) {
1455
+ ): ISerializedInterval | undefined {
1456
+ if (!this.client) {
1457
+ // If there's no associated mergeTree client, the originally submitted op is still correct.
1458
+ return serializedInterval;
1459
+ }
1424
1460
  if (!this.attached) {
1425
1461
  throw new LoggingError("attachSequence must be called");
1426
1462
  }
@@ -1432,6 +1468,8 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
1432
1468
  this.client.rebasePosition(end, sequenceNumber, localSeq);
1433
1469
 
1434
1470
  const intervalId = properties?.[reservedIntervalIdKey];
1471
+ const localInterval = this.localCollection.getIntervalById(intervalId);
1472
+
1435
1473
  const rebased: ISerializedInterval = {
1436
1474
  start: startRebased,
1437
1475
  end: endRebased,
@@ -1439,22 +1477,56 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
1439
1477
  sequenceNumber: this.client?.getCurrentSeq() ?? 0,
1440
1478
  properties,
1441
1479
  };
1480
+
1442
1481
  if (opName === "change" && (this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
1443
1482
  this.removePendingChange(serializedInterval);
1444
1483
  this.addPendingChange(intervalId, rebased);
1445
1484
  }
1485
+
1486
+ // if the interval slid off the string, rebase the op to be a noop and
1487
+ // delete the interval
1488
+ if (startRebased === DetachedReferencePosition || endRebased === DetachedReferencePosition) {
1489
+ if (localInterval) {
1490
+ this.localCollection.removeExistingInterval(localInterval);
1491
+ }
1492
+ return undefined;
1493
+ }
1494
+
1495
+ if (!localInterval) {
1496
+ return rebased;
1497
+ }
1498
+
1499
+ // we know we must be using `SequenceInterval` because `this.client` exists
1500
+ assert(
1501
+ localInterval instanceof SequenceInterval,
1502
+ 0x3a0 /* localInterval must be `SequenceInterval` when used with client */,
1503
+ );
1504
+
1505
+ const startSegment = this.getSlideToSegment(localInterval.start);
1506
+ const endSegment = this.getSlideToSegment(localInterval.end);
1507
+
1508
+ // we need to slide because the reference has been removed
1509
+ if (startSegment || endSegment) {
1510
+ const newStart =
1511
+ startSegment && this.client.getPosition(startSegment.segment, localSeq) + startSegment.offset;
1512
+ const newEnd =
1513
+ endSegment && this.client.getPosition(endSegment.segment, localSeq) + endSegment.offset;
1514
+
1515
+ this.localCollection.changeInterval(localInterval, newStart, newEnd, undefined, localSeq);
1516
+ }
1517
+
1446
1518
  return rebased;
1447
1519
  }
1448
1520
 
1449
- private getSlideToSegment(lref: LocalReference) {
1450
- const segoff = { segment: lref.segment, offset: lref.offset };
1521
+ private getSlideToSegment(lref: LocalReferencePosition) {
1522
+ const segoff = { segment: lref.getSegment(), offset: lref.getOffset() };
1451
1523
  const newSegoff = this.client.getSlideToSegment(segoff);
1452
1524
  const value: { segment: ISegment | undefined; offset: number | undefined; } | undefined
1453
1525
  = (segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset) ? undefined : newSegoff;
1454
1526
  return value;
1455
1527
  }
1456
1528
 
1457
- private setSlideOnRemove(lref: LocalReference) {
1529
+ private setSlideOnRemove(lref: LocalReferencePosition) {
1458
1530
  let refType = lref.refType;
1459
1531
  refType = refType & ~ReferenceType.StayOnRemove;
1460
1532
  refType = refType | ReferenceType.SlideOnRemove;
@@ -1462,7 +1534,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
1462
1534
  }
1463
1535
 
1464
1536
  private ackInterval(interval: TInterval, op: ISequencedDocumentMessage) {
1465
- // in current usage, interval is always a SequenceInterval
1537
+ // Only SequenceIntervals need potential sliding
1466
1538
  if (!(interval instanceof SequenceInterval)) {
1467
1539
  return;
1468
1540
  }
@@ -1493,7 +1565,7 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
1493
1565
  if (needsStartUpdate || needsEndUpdate) {
1494
1566
  // In this case, where we change the start or end of an interval,
1495
1567
  // it is necessary to remove and re-add the interval listeners.
1496
- // This ensures that the correct listeners are added to the ReferencePosition.
1568
+ // This ensures that the correct listeners are added to the LocalReferencePosition.
1497
1569
  this.localCollection.removeExistingInterval(interval);
1498
1570
 
1499
1571
  if (needsStartUpdate) {
@@ -1516,14 +1588,6 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
1516
1588
  }
1517
1589
  }
1518
1590
 
1519
- /** @deprecated - use ackAdd */
1520
- public addInternal(
1521
- serializedInterval: ISerializedInterval,
1522
- local: boolean,
1523
- op: ISequencedDocumentMessage) {
1524
- return this.ackAdd(serializedInterval, local, op);
1525
- }
1526
-
1527
1591
  /** @internal */
1528
1592
  public ackAdd(
1529
1593
  serializedInterval: ISerializedInterval,
@@ -1562,14 +1626,6 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
1562
1626
  return interval;
1563
1627
  }
1564
1628
 
1565
- /** @deprecated - use ackDelete */
1566
- public deleteInterval(
1567
- serializedInterval: ISerializedInterval,
1568
- local: boolean,
1569
- op: ISequencedDocumentMessage): void {
1570
- return this.ackDelete(serializedInterval, local, op);
1571
- }
1572
-
1573
1629
  /** @internal */
1574
1630
  public ackDelete(
1575
1631
  serializedInterval: ISerializedInterval,
@@ -1673,3 +1729,31 @@ export class IntervalCollection<TInterval extends ISerializableInterval>
1673
1729
  return this.localCollection.nextInterval(pos);
1674
1730
  }
1675
1731
  }
1732
+
1733
+ /**
1734
+ * Information that identifies an interval within a `Sequence`.
1735
+ */
1736
+ export interface IntervalLocator {
1737
+ /**
1738
+ * Label for the collection the interval is a part of
1739
+ */
1740
+ label: string;
1741
+ /**
1742
+ * Interval within that collection
1743
+ */
1744
+ interval: SequenceInterval;
1745
+ }
1746
+
1747
+ /**
1748
+ * Returns an object that can be used to find the interval a given LocalReferencePosition belongs to.
1749
+ * @returns undefined if the reference position is not the endpoint of any interval (e.g. it was created
1750
+ * on the merge tree directly by app code), otherwise an {@link IntervalLocator} for the interval this
1751
+ * endpoint is a part of.
1752
+ */
1753
+ export function intervalLocatorFromEndpoint(potentialEndpoint: LocalReferencePosition): IntervalLocator | undefined {
1754
+ const {
1755
+ interval,
1756
+ [reservedRangeLabelsKey]: collectionNameArray,
1757
+ } = potentialEndpoint.properties ?? {};
1758
+ return (interval && collectionNameArray?.length === 1) ? { label: collectionNameArray[0], interval } : undefined;
1759
+ }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/sequence";
9
- export const pkgVersion = "1.2.7";
9
+ export const pkgVersion = "2.0.0-dev.1.3.0.96595";
package/src/sequence.ts CHANGED
@@ -29,7 +29,6 @@ import {
29
29
  IRelativePosition,
30
30
  ISegment,
31
31
  ISegmentAction,
32
- LocalReference,
33
32
  LocalReferencePosition,
34
33
  matchProperties,
35
34
  MergeTreeDeltaType,
@@ -67,7 +66,7 @@ const contentPath = "content";
67
66
  /**
68
67
  * Events emitted in response to changes to the sequence data.
69
68
  *
70
- * @remarks
69
+ * @remarks
71
70
  *
72
71
  * The following is the list of events emitted.
73
72
  *
@@ -298,20 +297,6 @@ export abstract class SharedSegmentSequence<T extends ISegment>
298
297
  return this.client.getRangeExtentsOfPosition(pos);
299
298
  }
300
299
 
301
- /**
302
- * @deprecated - use createLocalReferencePosition
303
- */
304
- public createPositionReference(
305
- segment: T,
306
- offset: number,
307
- refType: ReferenceType): LocalReference {
308
- const lref = new LocalReference(this.client, segment, offset, refType);
309
- if (refType !== ReferenceType.Transient) {
310
- this.addLocalReference(lref);
311
- }
312
- return lref;
313
- }
314
-
315
300
  public createLocalReferencePosition(
316
301
  segment: T,
317
302
  offset: number,
@@ -324,13 +309,6 @@ export abstract class SharedSegmentSequence<T extends ISegment>
324
309
  properties);
325
310
  }
326
311
 
327
- /**
328
- * @deprecated - use localReferencePositionToPosition
329
- */
330
- public localRefToPos(localRef: LocalReference) {
331
- return this.client.localReferencePositionToPosition(localRef);
332
- }
333
-
334
312
  public localReferencePositionToPosition(lref: ReferencePosition): number {
335
313
  return this.client.localReferencePositionToPosition(lref);
336
314
  }
@@ -377,20 +355,6 @@ export abstract class SharedSegmentSequence<T extends ISegment>
377
355
  }
378
356
  }
379
357
 
380
- /**
381
- * @deprecated - use createLocalReferencePosition
382
- */
383
- public addLocalReference(lref: LocalReference) {
384
- return this.client.addLocalReference(lref);
385
- }
386
-
387
- /**
388
- * @deprecated - use removeLocalReferencePosition
389
- */
390
- public removeLocalReference(lref: LocalReference) {
391
- return this.client.removeLocalReferencePosition(lref);
392
- }
393
-
394
358
  public removeLocalReferencePosition(lref: LocalReferencePosition) {
395
359
  return this.client.removeLocalReferencePosition(lref);
396
360
  }
@@ -424,6 +388,10 @@ export abstract class SharedSegmentSequence<T extends ISegment>
424
388
  return this.client.walkSegments<TClientData>(handler, start, end, accum, splitRange);
425
389
  }
426
390
 
391
+ /**
392
+ * @deprecated for internal use only. public export will be removed.
393
+ * @internal
394
+ */
427
395
  public getStackContext(startPos: number, rangeLabels: string[]): RangeStackMap {
428
396
  return this.client.getStackContext(startPos, rangeLabels);
429
397
  }
@@ -440,8 +408,8 @@ export abstract class SharedSegmentSequence<T extends ISegment>
440
408
  }
441
409
 
442
410
  /**
443
- * @deprecated - IntervalCollections are created on a first-write wins basis, and concurrent creates
444
- * are supported. Use `getIntervalCollection` instead.
411
+ * @deprecated `IntervalCollection`s are created on a first-write wins basis, and concurrent creates
412
+ * are supported. Use {@link SharedSegmentSequence.getIntervalCollection} instead.
445
413
  */
446
414
  public async waitIntervalCollection(
447
415
  label: string,
@@ -454,12 +422,15 @@ export abstract class SharedSegmentSequence<T extends ISegment>
454
422
  }
455
423
 
456
424
  /**
457
- * @returns an iterable object that enumerates the IntervalCollection labels
458
- * Usage:
425
+ * @returns An iterable object that enumerates the IntervalCollection labels.
426
+ *
427
+ * @example
428
+ * ```typescript
459
429
  * const iter = this.getIntervalCollectionKeys();
460
430
  * for (key of iter)
461
431
  * const collection = this.getIntervalCollection(key);
462
432
  * ...
433
+ * ```
463
434
  */
464
435
  public getIntervalCollectionLabels(): IterableIterator<string> {
465
436
  return this.intervalCollections.keys();