@fluidframework/sequence 2.0.0-dev.5.3.2.178189 → 2.0.0-dev.6.4.0.191457

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 (225) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/README.md +4 -3
  3. package/dist/defaultMap.d.ts +1 -1
  4. package/dist/defaultMap.d.ts.map +1 -1
  5. package/dist/defaultMap.js +9 -10
  6. package/dist/defaultMap.js.map +1 -1
  7. package/dist/defaultMapInterfaces.d.ts +1 -1
  8. package/dist/defaultMapInterfaces.d.ts.map +1 -1
  9. package/dist/defaultMapInterfaces.js.map +1 -1
  10. package/dist/index.d.ts +3 -3
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +12 -9
  13. package/dist/index.js.map +1 -1
  14. package/dist/intervalCollection.d.ts +11 -424
  15. package/dist/intervalCollection.d.ts.map +1 -1
  16. package/dist/intervalCollection.js +74 -815
  17. package/dist/intervalCollection.js.map +1 -1
  18. package/dist/intervalIndex/endpointInRangeIndex.d.ts +20 -0
  19. package/dist/intervalIndex/endpointInRangeIndex.d.ts.map +1 -0
  20. package/dist/intervalIndex/endpointInRangeIndex.js +60 -0
  21. package/dist/intervalIndex/endpointInRangeIndex.js.map +1 -0
  22. package/dist/intervalIndex/endpointIndex.d.ts +21 -0
  23. package/dist/intervalIndex/endpointIndex.d.ts.map +1 -0
  24. package/dist/intervalIndex/endpointIndex.js +42 -0
  25. package/dist/intervalIndex/endpointIndex.js.map +1 -0
  26. package/dist/intervalIndex/idIntervalIndex.d.ts +12 -0
  27. package/dist/intervalIndex/idIntervalIndex.d.ts.map +1 -0
  28. package/dist/intervalIndex/idIntervalIndex.js +41 -0
  29. package/dist/intervalIndex/idIntervalIndex.js.map +1 -0
  30. package/dist/intervalIndex/index.d.ts +5 -0
  31. package/dist/intervalIndex/index.d.ts.map +1 -1
  32. package/dist/intervalIndex/index.js +9 -1
  33. package/dist/intervalIndex/index.js.map +1 -1
  34. package/dist/intervalIndex/intervalIndex.d.ts +29 -0
  35. package/dist/intervalIndex/intervalIndex.d.ts.map +1 -0
  36. package/dist/intervalIndex/intervalIndex.js +7 -0
  37. package/dist/intervalIndex/intervalIndex.js.map +1 -0
  38. package/dist/intervalIndex/intervalIndexUtils.d.ts +17 -0
  39. package/dist/intervalIndex/intervalIndexUtils.d.ts.map +1 -0
  40. package/dist/intervalIndex/intervalIndexUtils.js +22 -0
  41. package/dist/intervalIndex/intervalIndexUtils.js.map +1 -0
  42. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts +2 -1
  43. package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
  44. package/dist/intervalIndex/overlappingIntervalsIndex.js +3 -3
  45. package/dist/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  46. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js +5 -5
  47. package/dist/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -1
  48. package/dist/intervalIndex/sequenceIntervalIndexes.d.ts +1 -1
  49. package/dist/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -1
  50. package/dist/intervalIndex/sequenceIntervalIndexes.js.map +1 -1
  51. package/dist/intervalIndex/startpointInRangeIndex.d.ts +20 -0
  52. package/dist/intervalIndex/startpointInRangeIndex.d.ts.map +1 -0
  53. package/dist/intervalIndex/startpointInRangeIndex.js +62 -0
  54. package/dist/intervalIndex/startpointInRangeIndex.js.map +1 -0
  55. package/dist/intervalTree.d.ts +2 -56
  56. package/dist/intervalTree.d.ts.map +1 -1
  57. package/dist/intervalTree.js +2 -11
  58. package/dist/intervalTree.js.map +1 -1
  59. package/dist/intervals/index.d.ts +8 -0
  60. package/dist/intervals/index.d.ts.map +1 -0
  61. package/dist/intervals/index.js +23 -0
  62. package/dist/intervals/index.js.map +1 -0
  63. package/dist/intervals/interval.d.ts +88 -0
  64. package/dist/intervals/interval.d.ts.map +1 -0
  65. package/dist/intervals/interval.js +180 -0
  66. package/dist/intervals/interval.js.map +1 -0
  67. package/dist/intervals/intervalUtils.d.ts +200 -0
  68. package/dist/intervals/intervalUtils.d.ts.map +1 -0
  69. package/dist/intervals/intervalUtils.js +79 -0
  70. package/dist/intervals/intervalUtils.js.map +1 -0
  71. package/dist/intervals/sequenceInterval.d.ts +132 -0
  72. package/dist/intervals/sequenceInterval.d.ts.map +1 -0
  73. package/dist/intervals/sequenceInterval.js +313 -0
  74. package/dist/intervals/sequenceInterval.js.map +1 -0
  75. package/dist/packageVersion.d.ts +1 -1
  76. package/dist/packageVersion.js +1 -1
  77. package/dist/packageVersion.js.map +1 -1
  78. package/dist/revertibles.d.ts +1 -1
  79. package/dist/revertibles.d.ts.map +1 -1
  80. package/dist/revertibles.js +45 -52
  81. package/dist/revertibles.js.map +1 -1
  82. package/dist/sequence.d.ts +33 -4
  83. package/dist/sequence.d.ts.map +1 -1
  84. package/dist/sequence.js +91 -47
  85. package/dist/sequence.js.map +1 -1
  86. package/dist/sequenceDeltaEvent.d.ts +8 -3
  87. package/dist/sequenceDeltaEvent.d.ts.map +1 -1
  88. package/dist/sequenceDeltaEvent.js +3 -4
  89. package/dist/sequenceDeltaEvent.js.map +1 -1
  90. package/dist/sharedIntervalCollection.d.ts +2 -1
  91. package/dist/sharedIntervalCollection.d.ts.map +1 -1
  92. package/dist/sharedIntervalCollection.js +2 -2
  93. package/dist/sharedIntervalCollection.js.map +1 -1
  94. package/dist/sharedSequence.d.ts +9 -0
  95. package/dist/sharedSequence.d.ts.map +1 -1
  96. package/dist/sharedSequence.js +9 -6
  97. package/dist/sharedSequence.js.map +1 -1
  98. package/dist/sharedString.d.ts.map +1 -1
  99. package/dist/sharedString.js +9 -29
  100. package/dist/sharedString.js.map +1 -1
  101. package/lib/defaultMap.d.ts +1 -1
  102. package/lib/defaultMap.d.ts.map +1 -1
  103. package/lib/defaultMap.js +5 -6
  104. package/lib/defaultMap.js.map +1 -1
  105. package/lib/defaultMapInterfaces.d.ts +1 -1
  106. package/lib/defaultMapInterfaces.d.ts.map +1 -1
  107. package/lib/defaultMapInterfaces.js.map +1 -1
  108. package/lib/index.d.ts +3 -3
  109. package/lib/index.d.ts.map +1 -1
  110. package/lib/index.js +3 -2
  111. package/lib/index.js.map +1 -1
  112. package/lib/intervalCollection.d.ts +11 -424
  113. package/lib/intervalCollection.d.ts.map +1 -1
  114. package/lib/intervalCollection.js +43 -776
  115. package/lib/intervalCollection.js.map +1 -1
  116. package/lib/intervalIndex/endpointInRangeIndex.d.ts +20 -0
  117. package/lib/intervalIndex/endpointInRangeIndex.d.ts.map +1 -0
  118. package/lib/intervalIndex/endpointInRangeIndex.js +56 -0
  119. package/lib/intervalIndex/endpointInRangeIndex.js.map +1 -0
  120. package/lib/intervalIndex/endpointIndex.d.ts +21 -0
  121. package/lib/intervalIndex/endpointIndex.d.ts.map +1 -0
  122. package/lib/intervalIndex/endpointIndex.js +38 -0
  123. package/lib/intervalIndex/endpointIndex.js.map +1 -0
  124. package/lib/intervalIndex/idIntervalIndex.d.ts +12 -0
  125. package/lib/intervalIndex/idIntervalIndex.d.ts.map +1 -0
  126. package/lib/intervalIndex/idIntervalIndex.js +37 -0
  127. package/lib/intervalIndex/idIntervalIndex.js.map +1 -0
  128. package/lib/intervalIndex/index.d.ts +5 -0
  129. package/lib/intervalIndex/index.d.ts.map +1 -1
  130. package/lib/intervalIndex/index.js +4 -0
  131. package/lib/intervalIndex/index.js.map +1 -1
  132. package/lib/intervalIndex/intervalIndex.d.ts +29 -0
  133. package/lib/intervalIndex/intervalIndex.d.ts.map +1 -0
  134. package/lib/intervalIndex/intervalIndex.js +6 -0
  135. package/lib/intervalIndex/intervalIndex.js.map +1 -0
  136. package/lib/intervalIndex/intervalIndexUtils.d.ts +17 -0
  137. package/lib/intervalIndex/intervalIndexUtils.d.ts.map +1 -0
  138. package/lib/intervalIndex/intervalIndexUtils.js +18 -0
  139. package/lib/intervalIndex/intervalIndexUtils.js.map +1 -0
  140. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts +2 -1
  141. package/lib/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -1
  142. package/lib/intervalIndex/overlappingIntervalsIndex.js +1 -1
  143. package/lib/intervalIndex/overlappingIntervalsIndex.js.map +1 -1
  144. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js +1 -1
  145. package/lib/intervalIndex/overlappingSequenceIntervalsIndex.js.map +1 -1
  146. package/lib/intervalIndex/sequenceIntervalIndexes.d.ts +1 -1
  147. package/lib/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -1
  148. package/lib/intervalIndex/sequenceIntervalIndexes.js.map +1 -1
  149. package/lib/intervalIndex/startpointInRangeIndex.d.ts +20 -0
  150. package/lib/intervalIndex/startpointInRangeIndex.d.ts.map +1 -0
  151. package/lib/intervalIndex/startpointInRangeIndex.js +58 -0
  152. package/lib/intervalIndex/startpointInRangeIndex.js.map +1 -0
  153. package/lib/intervalTree.d.ts +2 -56
  154. package/lib/intervalTree.d.ts.map +1 -1
  155. package/lib/intervalTree.js +2 -11
  156. package/lib/intervalTree.js.map +1 -1
  157. package/lib/intervals/index.d.ts +8 -0
  158. package/lib/intervals/index.d.ts.map +1 -0
  159. package/lib/intervals/index.js +8 -0
  160. package/lib/intervals/index.js.map +1 -0
  161. package/lib/intervals/interval.d.ts +88 -0
  162. package/lib/intervals/interval.d.ts.map +1 -0
  163. package/lib/intervals/interval.js +175 -0
  164. package/lib/intervals/interval.js.map +1 -0
  165. package/lib/intervals/intervalUtils.d.ts +200 -0
  166. package/lib/intervals/intervalUtils.d.ts.map +1 -0
  167. package/lib/intervals/intervalUtils.js +74 -0
  168. package/lib/intervals/intervalUtils.js.map +1 -0
  169. package/lib/intervals/sequenceInterval.d.ts +132 -0
  170. package/lib/intervals/sequenceInterval.d.ts.map +1 -0
  171. package/lib/intervals/sequenceInterval.js +305 -0
  172. package/lib/intervals/sequenceInterval.js.map +1 -0
  173. package/lib/packageVersion.d.ts +1 -1
  174. package/lib/packageVersion.js +1 -1
  175. package/lib/packageVersion.js.map +1 -1
  176. package/lib/revertibles.d.ts +1 -1
  177. package/lib/revertibles.d.ts.map +1 -1
  178. package/lib/revertibles.js +28 -35
  179. package/lib/revertibles.js.map +1 -1
  180. package/lib/sequence.d.ts +33 -4
  181. package/lib/sequence.d.ts.map +1 -1
  182. package/lib/sequence.js +86 -41
  183. package/lib/sequence.js.map +1 -1
  184. package/lib/sequenceDeltaEvent.d.ts +8 -3
  185. package/lib/sequenceDeltaEvent.d.ts.map +1 -1
  186. package/lib/sequenceDeltaEvent.js +2 -3
  187. package/lib/sequenceDeltaEvent.js.map +1 -1
  188. package/lib/sharedIntervalCollection.d.ts +2 -1
  189. package/lib/sharedIntervalCollection.d.ts.map +1 -1
  190. package/lib/sharedIntervalCollection.js +1 -1
  191. package/lib/sharedIntervalCollection.js.map +1 -1
  192. package/lib/sharedSequence.d.ts +9 -0
  193. package/lib/sharedSequence.d.ts.map +1 -1
  194. package/lib/sharedSequence.js +8 -5
  195. package/lib/sharedSequence.js.map +1 -1
  196. package/lib/sharedString.d.ts.map +1 -1
  197. package/lib/sharedString.js +9 -29
  198. package/lib/sharedString.js.map +1 -1
  199. package/package.json +29 -32
  200. package/src/defaultMap.ts +2 -1
  201. package/src/defaultMapInterfaces.ts +1 -1
  202. package/src/index.ts +18 -12
  203. package/src/intervalCollection.ts +42 -1225
  204. package/src/intervalIndex/endpointInRangeIndex.ts +104 -0
  205. package/src/intervalIndex/endpointIndex.ts +78 -0
  206. package/src/intervalIndex/idIntervalIndex.ts +58 -0
  207. package/src/intervalIndex/index.ts +5 -0
  208. package/src/intervalIndex/intervalIndex.ts +31 -0
  209. package/src/intervalIndex/intervalIndexUtils.ts +27 -0
  210. package/src/intervalIndex/overlappingIntervalsIndex.ts +2 -6
  211. package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +1 -1
  212. package/src/intervalIndex/sequenceIntervalIndexes.ts +1 -1
  213. package/src/intervalIndex/startpointInRangeIndex.ts +109 -0
  214. package/src/intervalTree.ts +3 -75
  215. package/src/intervals/index.ts +25 -0
  216. package/src/intervals/interval.ts +230 -0
  217. package/src/intervals/intervalUtils.ts +256 -0
  218. package/src/intervals/sequenceInterval.ts +494 -0
  219. package/src/packageVersion.ts +1 -1
  220. package/src/revertibles.ts +24 -13
  221. package/src/sequence.ts +100 -35
  222. package/src/sequenceDeltaEvent.ts +12 -4
  223. package/src/sharedIntervalCollection.ts +2 -3
  224. package/src/sharedSequence.ts +11 -5
  225. package/src/sharedString.ts +8 -25
package/src/sequence.ts CHANGED
@@ -2,8 +2,9 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { Deferred, bufferToString, assert } from "@fluidframework/common-utils";
6
- import { ChildLogger } from "@fluidframework/telemetry-utils";
5
+ import { assert, Deferred } from "@fluidframework/core-utils";
6
+ import { bufferToString } from "@fluid-internal/client-utils";
7
+ import { LoggingError, createChildLogger } from "@fluidframework/telemetry-utils";
7
8
  import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol-definitions";
8
9
  import {
9
10
  IChannelAttributes,
@@ -13,6 +14,7 @@ import {
13
14
  import {
14
15
  Client,
15
16
  createAnnotateRangeOp,
17
+ // eslint-disable-next-line import/no-deprecated
16
18
  createGroupOp,
17
19
  createInsertOp,
18
20
  createRemoveRangeOp,
@@ -46,15 +48,14 @@ import {
46
48
  ISharedObjectEvents,
47
49
  SummarySerializer,
48
50
  } from "@fluidframework/shared-object-base";
49
- import { IEventThisPlaceHolder } from "@fluidframework/common-definitions";
51
+ import { IEventThisPlaceHolder } from "@fluidframework/core-interfaces";
50
52
  import { ISummaryTreeWithStats, ITelemetryContext } from "@fluidframework/runtime-definitions";
51
-
52
53
  import { DefaultMap, IMapOperation } from "./defaultMap";
53
54
  import { IMapMessageLocalMetadata, IValueChanged } from "./defaultMapInterfaces";
55
+ import { SequenceInterval } from "./intervals";
54
56
  import {
55
57
  IIntervalCollection,
56
58
  IntervalCollection,
57
- SequenceInterval,
58
59
  SequenceIntervalCollectionValueType,
59
60
  } from "./intervalCollection";
60
61
  import { SequenceDeltaEvent, SequenceMaintenanceEvent } from "./sequenceDeltaEvent";
@@ -119,6 +120,21 @@ export abstract class SharedSegmentSequence<T extends ISegment>
119
120
  return this.loadedDeferred.promise;
120
121
  }
121
122
 
123
+ /**
124
+ * This is a safeguard to avoid problematic reentrancy of local ops. This type of scenario occurs if the user of SharedString subscribes
125
+ * to the `sequenceDelta` event and uses the callback for a local op to submit further local ops.
126
+ * Historically (before 2.0.0-internal.6.1.0), doing so would result in eventual consistency issues or a corrupted document.
127
+ * These issues were fixed in #16815 which makes such reentrancy no different from applying the ops in order but not from within the change events,
128
+ * but there is still little test coverage for reentrant scenarios.
129
+ * Additionally, applications submitting ops from inside change events need to take extreme care that their data models also support reentrancy.
130
+ * Since this is likely not the case, by default SharedString throws when encountering reentrant ops.
131
+ *
132
+ * An application using SharedString which explicitly wants to opt in to allowing reentrancy anyway can set `sharedStringPreventReentrancy`
133
+ * on the data store options to `false`.
134
+ * @internal
135
+ */
136
+ protected guardReentrancy: <TRet>(callback: () => TRet) => TRet;
137
+
122
138
  private static createOpsFromDelta(event: SequenceDeltaEvent): IMergeTreeDeltaOp[] {
123
139
  const ops: IMergeTreeDeltaOp[] = [];
124
140
  for (const r of event.ranges) {
@@ -194,22 +210,38 @@ export abstract class SharedSegmentSequence<T extends ISegment>
194
210
  ) {
195
211
  super(id, dataStoreRuntime, attributes, "fluid_sequence_");
196
212
 
213
+ this.guardReentrancy =
214
+ dataStoreRuntime.options.sharedStringPreventReentrancy ?? true
215
+ ? ensureNoReentrancy
216
+ : createReentrancyDetector((depth) => {
217
+ if (totalReentrancyLogs > 0) {
218
+ totalReentrancyLogs--;
219
+ this.logger.sendTelemetryEvent(
220
+ { eventName: "LocalOpReentry", depth },
221
+ new LoggingError(reentrancyErrorMessage),
222
+ );
223
+ }
224
+ });
225
+
197
226
  this.loadedDeferred.promise.catch((error) => {
198
227
  this.logger.sendErrorEvent({ eventName: "SequenceLoadFailed" }, error);
199
228
  });
200
229
 
201
230
  this.client = new Client(
202
231
  segmentFromSpec,
203
- ChildLogger.create(this.logger, "SharedSegmentSequence.MergeTreeClient"),
232
+ createChildLogger({
233
+ logger: this.logger,
234
+ namespace: "SharedSegmentSequence.MergeTreeClient",
235
+ }),
204
236
  dataStoreRuntime.options,
205
237
  );
206
238
 
207
- this.client.on("delta", (opArgs, deltaArgs) => {
208
- this.emit(
209
- "sequenceDelta",
210
- new SequenceDeltaEvent(opArgs, deltaArgs, this.client),
211
- this,
212
- );
239
+ this.client.prependListener("delta", (opArgs, deltaArgs) => {
240
+ const event = new SequenceDeltaEvent(opArgs, deltaArgs, this.client);
241
+ if (opArgs.stashed !== true && event.isLocal) {
242
+ this.submitSequenceMessage(opArgs.op);
243
+ }
244
+ this.emit("sequenceDelta", event, this);
213
245
  });
214
246
 
215
247
  this.client.on("maintenance", (args, opArgs) => {
@@ -230,14 +262,14 @@ export abstract class SharedSegmentSequence<T extends ISegment>
230
262
  * @param end - The exclusive end of the range to remove
231
263
  */
232
264
  public removeRange(start: number, end: number): IMergeTreeRemoveMsg {
233
- const removeOp = this.client.removeRangeLocal(start, end);
234
- this.submitSequenceMessage(removeOp);
235
- return removeOp;
265
+ return this.guardReentrancy(() => this.client.removeRangeLocal(start, end));
236
266
  }
237
267
 
268
+ /**
269
+ * @deprecated - The ability to create group ops will be removed in an upcoming release, as group ops are redundant with the native batching capabilities of the runtime
270
+ */
238
271
  public groupOperation(groupOp: IMergeTreeGroupMsg) {
239
- this.client.localTransaction(groupOp);
240
- this.submitSequenceMessage(groupOp);
272
+ this.guardReentrancy(() => this.client.localTransaction(groupOp));
241
273
  }
242
274
 
243
275
  /**
@@ -283,10 +315,7 @@ export abstract class SharedSegmentSequence<T extends ISegment>
283
315
  props: PropertySet,
284
316
  combiningOp?: ICombiningOp,
285
317
  ) {
286
- const annotateOp = this.client.annotateRangeLocal(start, end, props, combiningOp);
287
- if (annotateOp) {
288
- this.submitSequenceMessage(annotateOp);
289
- }
318
+ this.guardReentrancy(() => this.client.annotateRangeLocal(start, end, props, combiningOp));
290
319
  }
291
320
 
292
321
  public getPropertiesAtPosition(pos: number) {
@@ -361,6 +390,9 @@ export abstract class SharedSegmentSequence<T extends ISegment>
361
390
  );
362
391
  }
363
392
 
393
+ /**
394
+ * @deprecated - This method will no longer be public in an upcoming release as it is not safe to use outside of this class
395
+ */
364
396
  public submitSequenceMessage(message: IMergeTreeOp) {
365
397
  if (!this.isAttached()) {
366
398
  return;
@@ -412,12 +444,15 @@ export abstract class SharedSegmentSequence<T extends ISegment>
412
444
  this.client.walkSegments(handler, start, end, accum as TClientData, splitRange);
413
445
  }
414
446
 
447
+ /**
448
+ * @deprecated - this functionality is no longer supported and will be removed
449
+ */
415
450
  public getStackContext(startPos: number, rangeLabels: string[]): RangeStackMap {
416
451
  return this.client.getStackContext(startPos, rangeLabels);
417
452
  }
418
453
 
419
454
  /**
420
- * @returns - The most recent sequence number which has been acked by the server and processed by this
455
+ * @returns The most recent sequence number which has been acked by the server and processed by this
421
456
  * SharedSegmentSequence.
422
457
  */
423
458
  public getCurrentSeq() {
@@ -430,10 +465,7 @@ export abstract class SharedSegmentSequence<T extends ISegment>
430
465
  * @param segment - The segment to insert
431
466
  */
432
467
  public insertAtReferencePosition(pos: ReferencePosition, segment: T) {
433
- const insertOp = this.client.insertAtReferencePositionLocal(pos, segment);
434
- if (insertOp) {
435
- this.submitSequenceMessage(insertOp);
436
- }
468
+ this.guardReentrancy(() => this.client.insertAtReferencePositionLocal(pos, segment));
437
469
  }
438
470
  /**
439
471
  * Inserts a segment
@@ -442,10 +474,7 @@ export abstract class SharedSegmentSequence<T extends ISegment>
442
474
  */
443
475
  public insertFromSpec(pos: number, spec: IJSONSegment) {
444
476
  const segment = this.segmentFromSpec(spec);
445
- const insertOp = this.client.insertSegmentLocal(pos, segment);
446
- if (insertOp) {
447
- this.submitSequenceMessage(insertOp);
448
- }
477
+ this.guardReentrancy(() => this.client.insertSegmentLocal(pos, segment));
449
478
  }
450
479
 
451
480
  /**
@@ -460,6 +489,7 @@ export abstract class SharedSegmentSequence<T extends ISegment>
460
489
  * @returns An iterable object that enumerates the IntervalCollection labels.
461
490
  *
462
491
  * @example
492
+ *
463
493
  * ```typescript
464
494
  * const iter = this.getIntervalCollectionKeys();
465
495
  * for (key of iter)
@@ -520,11 +550,7 @@ export abstract class SharedSegmentSequence<T extends ISegment>
520
550
  const insert = this.client.insertSegmentLocal(insertIndex, segment);
521
551
  if (insert) {
522
552
  if (start < end) {
523
- const remove = this.client.removeRangeLocal(start, end);
524
- const op = remove ? createGroupOp(insert, remove) : insert;
525
- this.submitSequenceMessage(op);
526
- } else {
527
- this.submitSequenceMessage(insert);
553
+ this.client.removeRangeLocal(start, end);
528
554
  }
529
555
  }
530
556
  }
@@ -729,6 +755,7 @@ export abstract class SharedSegmentSequence<T extends ISegment>
729
755
  stashMessage = {
730
756
  ...message,
731
757
  referenceSequenceNumber: stashMessage.sequenceNumber - 1,
758
+ // eslint-disable-next-line import/no-deprecated
732
759
  contents: ops.length !== 1 ? createGroupOp(...ops) : ops[0],
733
760
  };
734
761
  }
@@ -810,3 +837,41 @@ export abstract class SharedSegmentSequence<T extends ISegment>
810
837
  }
811
838
  }
812
839
  }
840
+
841
+ function createReentrancyDetector(
842
+ onReentrancy: (depth: number) => void,
843
+ ): <T>(callback: () => T) => T {
844
+ let depth = 0;
845
+ function detectReentrancy<T>(callback: () => T): T {
846
+ if (depth > 0) {
847
+ onReentrancy(depth);
848
+ }
849
+ depth++;
850
+ try {
851
+ return callback();
852
+ } finally {
853
+ depth--;
854
+ }
855
+ }
856
+
857
+ return detectReentrancy;
858
+ }
859
+
860
+ /**
861
+ * Apps which generate reentrant behavior may do so at a high frequency.
862
+ * Logging even per-SharedSegmentSequence instance might be too noisy, and having a few logs from a session
863
+ * is likely enough.
864
+ */
865
+ let totalReentrancyLogs = 3;
866
+
867
+ /**
868
+ * Resets the reentrancy log counter. Test-only API.
869
+ */
870
+ export function resetReentrancyLogCounter() {
871
+ totalReentrancyLogs = 3;
872
+ }
873
+
874
+ const reentrancyErrorMessage = "Reentrancy detected in sequence local ops";
875
+ const ensureNoReentrancy = createReentrancyDetector(() => {
876
+ throw new LoggingError(reentrancyErrorMessage);
877
+ });
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { assert } from "@fluidframework/common-utils";
6
+ import { assert } from "@fluidframework/core-utils";
7
7
  import {
8
8
  Client,
9
9
  IMergeTreeDeltaCallbackArgs,
@@ -147,25 +147,33 @@ export interface ISequenceDeltaRange<
147
147
  > {
148
148
  /**
149
149
  * The type of operation that changed this range.
150
- * @remarks - Consuming code should typically compare this to the enum values defined in
150
+ *
151
+ * @remarks Consuming code should typically compare this to the enum values defined in
151
152
  * `MergeTreeDeltaOperationTypes`.
152
153
  */
153
154
  operation: TOperation;
155
+
154
156
  /**
155
157
  * The index of the start of the range.
156
158
  */
157
159
  position: number;
160
+
158
161
  /**
159
162
  * The segment that corresponds to the range.
160
163
  */
161
164
  segment: ISegment;
165
+
162
166
  /**
163
167
  * Deltas object which contains all modified properties with their previous values.
164
168
  * Since `undefined` doesn't survive a round-trip through JSON serialization, the old value being absent
165
169
  * is instead encoded with `null`.
166
- * @remarks - This object is motivated by undo/redo scenarios, and provides a convenient "inverse op" to apply to
170
+ *
171
+ * @remarks This object is motivated by undo/redo scenarios, and provides a convenient "inverse op" to apply to
167
172
  * undo a property change.
168
- * @example - If a segment initially had properties `{ foo: "1", bar: 2 }` and it was annotated with
173
+ *
174
+ * @example
175
+ *
176
+ * If a segment initially had properties `{ foo: "1", bar: 2 }` and it was annotated with
169
177
  * `{ foo: 3, baz: 5 }`, the corresponding event would have a `propertyDeltas` of `{ foo: "1", baz: null }`.
170
178
  */
171
179
  propertyDeltas: PropertySet;
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { bufferToString } from "@fluidframework/common-utils";
6
+ import { bufferToString } from "@fluid-internal/client-utils";
7
7
  import {
8
8
  IChannelAttributes,
9
9
  IFluidDataStoreRuntime,
@@ -18,12 +18,11 @@ import {
18
18
  IFluidSerializer,
19
19
  SharedObject,
20
20
  } from "@fluidframework/shared-object-base";
21
+ import { Interval, ISerializableInterval } from "./intervals";
21
22
  import {
22
- Interval,
23
23
  IntervalCollection,
24
24
  IIntervalCollection,
25
25
  IntervalCollectionValueType,
26
- ISerializableInterval,
27
26
  } from "./intervalCollection";
28
27
  import { DefaultMap, IMapOperation } from "./defaultMap";
29
28
  import { pkgVersion } from "./packageVersion";
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { assert } from "@fluidframework/common-utils";
6
+ import { assert } from "@fluidframework/core-utils";
7
7
  import { BaseSegment, IJSONSegment, ISegment, PropertySet } from "@fluidframework/merge-tree";
8
8
  import {
9
9
  IChannelAttributes,
@@ -14,10 +14,16 @@ import { SharedSegmentSequence } from "./sequence";
14
14
 
15
15
  const MaxRun = 128;
16
16
 
17
+ /**
18
+ * @deprecated - IJSONRunSegment will be removed in a upcoming release. It has been moved to the fluid-experimental/sequence-deprecated package
19
+ */
17
20
  export interface IJSONRunSegment<T> extends IJSONSegment {
18
21
  items: Serializable<T>[];
19
22
  }
20
23
 
24
+ /**
25
+ * @deprecated - SubSequence will be removed in a upcoming release. It has been moved to the fluid-experimental/sequence-deprecated package
26
+ */
21
27
  export class SubSequence<T> extends BaseSegment {
22
28
  public static readonly typeString: string = "SubSequence";
23
29
  public static is(segment: ISegment): segment is SubSequence<any> {
@@ -98,6 +104,9 @@ export class SubSequence<T> extends BaseSegment {
98
104
  }
99
105
  }
100
106
 
107
+ /**
108
+ * @deprecated - SharedSequence will be removed in a upcoming release. It has been moved to the fluid-experimental/sequence-deprecated package
109
+ */
101
110
  export class SharedSequence<T> extends SharedSegmentSequence<SubSequence<T>> {
102
111
  constructor(
103
112
  document: IFluidDataStoreRuntime,
@@ -118,10 +127,7 @@ export class SharedSequence<T> extends SharedSegmentSequence<SubSequence<T>> {
118
127
  if (props) {
119
128
  segment.addProperties(props);
120
129
  }
121
- const insertOp = this.client.insertSegmentLocal(pos, segment);
122
- if (insertOp) {
123
- this.submitSequenceMessage(insertOp);
124
- }
130
+ this.client.insertSegmentLocal(pos, segment);
125
131
  }
126
132
 
127
133
  /**
@@ -118,10 +118,7 @@ export class SharedString
118
118
  }
119
119
 
120
120
  const pos = this.posFromRelativePos(relativePos1);
121
- const insertOp = this.client.insertSegmentLocal(pos, segment);
122
- if (insertOp) {
123
- this.submitSequenceMessage(insertOp);
124
- }
121
+ this.guardReentrancy(() => this.client.insertSegmentLocal(pos, segment));
125
122
  }
126
123
 
127
124
  /**
@@ -137,11 +134,7 @@ export class SharedString
137
134
  segment.addProperties(props);
138
135
  }
139
136
 
140
- const insertOp = this.client.insertSegmentLocal(pos, segment);
141
- if (insertOp) {
142
- this.submitSequenceMessage(insertOp);
143
- }
144
- return insertOp;
137
+ return this.guardReentrancy(() => this.client.insertSegmentLocal(pos, segment));
145
138
  }
146
139
 
147
140
  /**
@@ -157,10 +150,7 @@ export class SharedString
157
150
  }
158
151
 
159
152
  const pos = this.posFromRelativePos(relativePos1);
160
- const insertOp = this.client.insertSegmentLocal(pos, segment);
161
- if (insertOp) {
162
- this.submitSequenceMessage(insertOp);
163
- }
153
+ this.guardReentrancy(() => this.client.insertSegmentLocal(pos, segment));
164
154
  }
165
155
 
166
156
  /**
@@ -172,10 +162,7 @@ export class SharedString
172
162
  segment.addProperties(props);
173
163
  }
174
164
 
175
- const insertOp = this.client.insertSegmentLocal(pos, segment);
176
- if (insertOp) {
177
- this.submitSequenceMessage(insertOp);
178
- }
165
+ this.guardReentrancy(() => this.client.insertSegmentLocal(pos, segment));
179
166
  }
180
167
 
181
168
  /**
@@ -210,10 +197,9 @@ export class SharedString
210
197
  props: PropertySet,
211
198
  callback: (m: Marker) => void,
212
199
  ) {
213
- const annotateOp = this.client.annotateMarkerNotifyConsensus(marker, props, callback);
214
- if (annotateOp) {
215
- this.submitSequenceMessage(annotateOp);
216
- }
200
+ this.guardReentrancy(() =>
201
+ this.client.annotateMarkerNotifyConsensus(marker, props, callback),
202
+ );
217
203
  }
218
204
 
219
205
  /**
@@ -223,10 +209,7 @@ export class SharedString
223
209
  * @param combiningOp - Optional. Specifies how to combine values for the property, such as "incr" for increment.
224
210
  */
225
211
  public annotateMarker(marker: Marker, props: PropertySet, combiningOp?: ICombiningOp) {
226
- const annotateOp = this.client.annotateMarker(marker, props, combiningOp);
227
- if (annotateOp) {
228
- this.submitSequenceMessage(annotateOp);
229
- }
212
+ this.guardReentrancy(() => this.client.annotateMarker(marker, props, combiningOp));
230
213
  }
231
214
 
232
215
  /**