@fluidframework/sequence 2.0.0-internal.3.0.1 → 2.0.0-internal.3.1.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.
Files changed (94) hide show
  1. package/.eslintrc.js +9 -12
  2. package/.mocharc.js +2 -2
  3. package/.vscode/launch.json +15 -14
  4. package/README.md +188 -179
  5. package/api-extractor.json +2 -2
  6. package/dist/defaultMap.d.ts.map +1 -1
  7. package/dist/defaultMap.js +5 -4
  8. package/dist/defaultMap.js.map +1 -1
  9. package/dist/defaultMapInterfaces.d.ts.map +1 -1
  10. package/dist/defaultMapInterfaces.js.map +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/intervalCollection.d.ts.map +1 -1
  15. package/dist/intervalCollection.js +50 -36
  16. package/dist/intervalCollection.js.map +1 -1
  17. package/dist/intervalTree.d.ts.map +1 -1
  18. package/dist/intervalTree.js.map +1 -1
  19. package/dist/localValues.d.ts.map +1 -1
  20. package/dist/localValues.js.map +1 -1
  21. package/dist/packageVersion.d.ts +1 -1
  22. package/dist/packageVersion.js +1 -1
  23. package/dist/packageVersion.js.map +1 -1
  24. package/dist/sequence.d.ts +1 -1
  25. package/dist/sequence.d.ts.map +1 -1
  26. package/dist/sequence.js +13 -17
  27. package/dist/sequence.js.map +1 -1
  28. package/dist/sequenceDeltaEvent.d.ts.map +1 -1
  29. package/dist/sequenceDeltaEvent.js.map +1 -1
  30. package/dist/sequenceFactory.d.ts.map +1 -1
  31. package/dist/sequenceFactory.js.map +1 -1
  32. package/dist/sharedIntervalCollection.d.ts.map +1 -1
  33. package/dist/sharedIntervalCollection.js.map +1 -1
  34. package/dist/sharedSequence.d.ts.map +1 -1
  35. package/dist/sharedSequence.js +3 -3
  36. package/dist/sharedSequence.js.map +1 -1
  37. package/dist/sharedString.d.ts.map +1 -1
  38. package/dist/sharedString.js +5 -4
  39. package/dist/sharedString.js.map +1 -1
  40. package/lib/defaultMap.d.ts.map +1 -1
  41. package/lib/defaultMap.js +6 -5
  42. package/lib/defaultMap.js.map +1 -1
  43. package/lib/defaultMapInterfaces.d.ts.map +1 -1
  44. package/lib/defaultMapInterfaces.js.map +1 -1
  45. package/lib/index.d.ts +2 -2
  46. package/lib/index.d.ts.map +1 -1
  47. package/lib/index.js +2 -2
  48. package/lib/index.js.map +1 -1
  49. package/lib/intervalCollection.d.ts.map +1 -1
  50. package/lib/intervalCollection.js +50 -36
  51. package/lib/intervalCollection.js.map +1 -1
  52. package/lib/intervalTree.d.ts.map +1 -1
  53. package/lib/intervalTree.js.map +1 -1
  54. package/lib/localValues.d.ts.map +1 -1
  55. package/lib/localValues.js +1 -1
  56. package/lib/localValues.js.map +1 -1
  57. package/lib/packageVersion.d.ts +1 -1
  58. package/lib/packageVersion.js +1 -1
  59. package/lib/packageVersion.js.map +1 -1
  60. package/lib/sequence.d.ts +1 -1
  61. package/lib/sequence.d.ts.map +1 -1
  62. package/lib/sequence.js +15 -19
  63. package/lib/sequence.js.map +1 -1
  64. package/lib/sequenceDeltaEvent.d.ts.map +1 -1
  65. package/lib/sequenceDeltaEvent.js.map +1 -1
  66. package/lib/sequenceFactory.d.ts.map +1 -1
  67. package/lib/sequenceFactory.js +1 -1
  68. package/lib/sequenceFactory.js.map +1 -1
  69. package/lib/sharedIntervalCollection.d.ts.map +1 -1
  70. package/lib/sharedIntervalCollection.js.map +1 -1
  71. package/lib/sharedSequence.d.ts.map +1 -1
  72. package/lib/sharedSequence.js +4 -4
  73. package/lib/sharedSequence.js.map +1 -1
  74. package/lib/sharedString.d.ts.map +1 -1
  75. package/lib/sharedString.js +5 -4
  76. package/lib/sharedString.js.map +1 -1
  77. package/package.json +121 -120
  78. package/prettier.config.cjs +1 -1
  79. package/src/defaultMap.ts +406 -405
  80. package/src/defaultMapInterfaces.ts +120 -115
  81. package/src/index.ts +27 -17
  82. package/src/intervalCollection.ts +2198 -1997
  83. package/src/intervalTree.ts +139 -139
  84. package/src/localValues.ts +64 -73
  85. package/src/packageVersion.ts +1 -1
  86. package/src/sequence.ts +739 -694
  87. package/src/sequenceDeltaEvent.ts +143 -137
  88. package/src/sequenceFactory.ts +48 -46
  89. package/src/sharedIntervalCollection.ts +150 -136
  90. package/src/sharedSequence.ts +165 -160
  91. package/src/sharedString.ts +385 -343
  92. package/tsconfig.esnext.json +6 -6
  93. package/tsconfig.json +8 -12
  94. package/.editorconfig +0 -7
package/.eslintrc.js CHANGED
@@ -4,15 +4,12 @@
4
4
  */
5
5
 
6
6
  module.exports = {
7
- "extends": [
8
- require.resolve("@fluidframework/eslint-config-fluid/minimal"),
9
- "prettier"
10
- ],
11
- "parserOptions": {
12
- "project": ["./tsconfig.json", "./src/test/tsconfig.json"]
13
- },
14
- "rules": {
15
- "@typescript-eslint/no-use-before-define": "off",
16
- "@typescript-eslint/strict-boolean-expressions": "off"
17
- }
18
- }
7
+ extends: [require.resolve("@fluidframework/eslint-config-fluid/minimal"), "prettier"],
8
+ parserOptions: {
9
+ project: ["./tsconfig.json", "./src/test/tsconfig.json"],
10
+ },
11
+ rules: {
12
+ "@typescript-eslint/no-use-before-define": "off",
13
+ "@typescript-eslint/strict-boolean-expressions": "off",
14
+ },
15
+ };
package/.mocharc.js CHANGED
@@ -3,9 +3,9 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- 'use strict';
6
+ "use strict";
7
7
 
8
- const getFluidTestMochaConfig = require('@fluidframework/mocha-test-setup/mocharc-common');
8
+ const getFluidTestMochaConfig = require("@fluidframework/mocha-test-setup/mocharc-common");
9
9
 
10
10
  const packageDir = __dirname;
11
11
  const config = getFluidTestMochaConfig(packageDir);
@@ -1,15 +1,16 @@
1
1
  {
2
- // Use IntelliSense to learn about possible attributes.
3
- // Hover to view descriptions of existing attributes.
4
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
- "version": "0.2.0",
6
- "configurations": [
7
- {
8
- "name": "Sequence Test",
9
- "type": "node",
10
- "request": "launch",
11
- "program": "${workspaceRoot}/../../../node_modules/mocha/bin/_mocha",
12
- "args": ["dist/test", "--no-timeouts", "--exit"],
13
- "cwd": "${workspaceRoot}", }
14
- ]
15
- }
2
+ // Use IntelliSense to learn about possible attributes.
3
+ // Hover to view descriptions of existing attributes.
4
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": [
7
+ {
8
+ "name": "Sequence Test",
9
+ "type": "node",
10
+ "request": "launch",
11
+ "program": "${workspaceRoot}/../../../node_modules/mocha/bin/_mocha",
12
+ "args": ["dist/test", "--no-timeouts", "--exit"],
13
+ "cwd": "${workspaceRoot}"
14
+ }
15
+ ]
16
+ }
package/README.md CHANGED
@@ -6,20 +6,20 @@ Its main export is [SharedString][], a DDS for storing and simultaneously editin
6
6
  Note that SharedString is a sequence DDS but it has additional specialized features and behaviors for working with text.
7
7
 
8
8
  This package historically contained several other sequence-based DDSes, but because they have unintuitive behaviors,
9
- they are deprecated and being moved to the *experimental* folder.
9
+ they are deprecated and being moved to the _experimental_ folder.
10
10
 
11
- The main reason for this is the lack of *move* semantics within the sequence, which becomes crucial when dealing with sequences of
11
+ The main reason for this is the lack of _move_ semantics within the sequence, which becomes crucial when dealing with sequences of
12
12
  complex content.
13
13
  For that reason, all of the examples in this README use `SharedString`. However, the APIs discussed are available on the common base class: `SharedSegmentSequence`.
14
14
 
15
- For the remainder of this document, the term *sequence* will refer to this base class.
15
+ For the remainder of this document, the term _sequence_ will refer to this base class.
16
16
 
17
17
  *Item*s are the individual units that are stored within the sequence (e.g. in a SharedString, the items are characters),
18
- but regardless of the type of data stored in the sequence, every item in a sequence is at a specific *position* starting
18
+ but regardless of the type of data stored in the sequence, every item in a sequence is at a specific _position_ starting
19
19
  at 0, similar to an array. However, sequences differ from arrays in that the positions can move as local and remote
20
20
  editors make modifications to the sequence.
21
21
 
22
- As its name suggests, SharedSegmentSequence is composed of *segments*. Segments are the unit that the sequence works
22
+ As its name suggests, SharedSegmentSequence is composed of _segments_. Segments are the unit that the sequence works
23
23
  with internally, and contain items within them. Thus, every segment has a length of at least 1 -- that is, it contains
24
24
  at least one item -- and segments may be split and merged arbitrarily as the sequence is edited. This means the length
25
25
  of the sequence is not the number of segments, but rather the sum of the length of all the segments.
@@ -63,49 +63,45 @@ can be any position in the sequence including 0, to insert at the beginning of t
63
63
  sequence, to insert at the end.
64
64
 
65
65
  ```typescript
66
- // content:
67
- // positions:
68
-
69
- // insert text at position 0
70
- sharedString.insertText(0, "hi");
71
- // content: hi
72
- // positions: 01
73
-
74
- // insert text at the end position
75
- sharedString.insertText(
76
- sharedString.getLength(),
77
- "!");
78
- // content: hi!
79
- // positions: 012
80
-
81
- // insert text at position 2
82
- sharedString.insertText(
83
- 2,
84
- " world");
85
- // content: hi world!
86
- // positions: 012345678
66
+ // content:
67
+ // positions:
68
+
69
+ // insert text at position 0
70
+ sharedString.insertText(0, "hi");
71
+ // content: hi
72
+ // positions: 01
73
+
74
+ // insert text at the end position
75
+ sharedString.insertText(sharedString.getLength(), "!");
76
+ // content: hi!
77
+ // positions: 012
78
+
79
+ // insert text at position 2
80
+ sharedString.insertText(2, " world");
81
+ // content: hi world!
82
+ // positions: 012345678
87
83
  ```
88
84
 
89
85
  ### Remove
90
86
 
91
- Remove operations take a start and an end position, referred to as a *range*. The start position is inclusive and can be
87
+ Remove operations take a start and an end position, referred to as a _range_. The start position is inclusive and can be
92
88
  any position in the sequence from 0 to its `length - 1`. The start position cannot be the length of the sequence like it
93
89
  can in insert, because there is nothing at that position. The end position is exclusive and must be greater than the
94
- start, so it can be any value from 1 to *n* (where *n* is the length of the sequence).
90
+ start, so it can be any value from 1 to _n_ (where _n_ is the length of the sequence).
95
91
 
96
92
  ```typescript
97
- // content: hi world!
98
- // positions: 012345678
99
-
100
- // remove the first 3 characters
101
- sharedString.removeRange(0, 3);
102
- // content: world!
103
- // positions: 012345
104
-
105
- // remove all the characters
106
- sharedString.removeRange(0, sharedString.getLength());
107
- // content:
108
- // positions:
93
+ // content: hi world!
94
+ // positions: 012345678
95
+
96
+ // remove the first 3 characters
97
+ sharedString.removeRange(0, 3);
98
+ // content: world!
99
+ // positions: 012345
100
+
101
+ // remove all the characters
102
+ sharedString.removeRange(0, sharedString.getLength());
103
+ // content:
104
+ // positions:
109
105
  ```
110
106
 
111
107
  ### Annotate
@@ -117,64 +113,58 @@ also takes a map-like properties object. Each key of the provided properties obj
117
113
  specified range. Setting a property key to null will remove that property from the positions in the range.
118
114
 
119
115
  ```typescript
120
- // content: hi world
121
- // positions: 01234567
122
-
123
- let props1 = sharedString.getPropertiesAtPosition(1);
124
- let props5 = sharedString.getPropertiesAtPosition(5);
125
- // props1 = {}
126
- // props5 = {}
127
-
128
- // set property called weight on positions 0 and 1
129
- sharedString.annotateRange(0, 2, { weight: 5 });
130
- props1 = sharedString.getPropertiesAtPosition(1);
131
- props5 = sharedString.getPropertiesAtPosition(5);
132
- // props1 = { weight: 5 }
133
- // props5 = {}
134
-
135
- // set property called decoration on all positions
136
- sharedString.annotateRange(
137
- 0,
138
- sharedString.getLength(),
139
- { decoration: "underline" });
140
- props1 = sharedString.getPropertiesAtPosition(1);
141
- props5 = sharedString.getPropertiesAtPosition(5);
142
- // props1 = { weight: 5, decoration: "underline" }
143
- // props5 = { decoration: "underline" }
144
-
145
- // remove property called weight on all positions
146
- sharedString.annotateRange(
147
- 0,
148
- sharedString.getLength(),
149
- { weight: null });
150
- props1 = sharedString.getPropertiesAtPosition(1);
151
- props5 = sharedString.getPropertiesAtPosition(5);
152
- // props1 = { decoration: "underline" }
153
- // props5 = { decoration: "underline" }
116
+ // content: hi world
117
+ // positions: 01234567
118
+
119
+ let props1 = sharedString.getPropertiesAtPosition(1);
120
+ let props5 = sharedString.getPropertiesAtPosition(5);
121
+ // props1 = {}
122
+ // props5 = {}
123
+
124
+ // set property called weight on positions 0 and 1
125
+ sharedString.annotateRange(0, 2, { weight: 5 });
126
+ props1 = sharedString.getPropertiesAtPosition(1);
127
+ props5 = sharedString.getPropertiesAtPosition(5);
128
+ // props1 = { weight: 5 }
129
+ // props5 = {}
130
+
131
+ // set property called decoration on all positions
132
+ sharedString.annotateRange(0, sharedString.getLength(), { decoration: "underline" });
133
+ props1 = sharedString.getPropertiesAtPosition(1);
134
+ props5 = sharedString.getPropertiesAtPosition(5);
135
+ // props1 = { weight: 5, decoration: "underline" }
136
+ // props5 = { decoration: "underline" }
137
+
138
+ // remove property called weight on all positions
139
+ sharedString.annotateRange(0, sharedString.getLength(), { weight: null });
140
+ props1 = sharedString.getPropertiesAtPosition(1);
141
+ props5 = sharedString.getPropertiesAtPosition(5);
142
+ // props1 = { decoration: "underline" }
143
+ // props5 = { decoration: "underline" }
154
144
  ```
155
145
 
156
146
  ### Sequence delta event
157
147
 
158
- Whenever an operation is performed on a sequence a *sequenceDelta* event will be raised. This event provides the ranges
148
+ Whenever an operation is performed on a sequence a _sequenceDelta_ event will be raised. This event provides the ranges
159
149
  affected by the operation, the type of the operation, and the properties that were changed by the operation.
160
150
 
161
151
  ```typescript
162
152
  sharedString.on("sequenceDelta", ({ deltaOperation, ranges, isLocal }) => {
163
- if (isLocal) {
164
- // undo-redo implementations frequently will only concern themselves with local ops: only operations submitted
165
- // by the local client should be undoable by the current user
166
- addOperationToUndoStack(deltaOperation, ranges);
167
- }
153
+ if (isLocal) {
154
+ // undo-redo implementations frequently will only concern themselves with local ops: only operations submitted
155
+ // by the local client should be undoable by the current user
156
+ addOperationToUndoStack(deltaOperation, ranges);
157
+ }
168
158
 
169
- if (deltaOperation === MergeTreeDeltaType.INSERT) {
170
- syncInsertSegmentToModel(deltaOperation, ranges);
171
- }
159
+ if (deltaOperation === MergeTreeDeltaType.INSERT) {
160
+ syncInsertSegmentToModel(deltaOperation, ranges);
161
+ }
172
162
 
173
- // realistic app code would likely handle the other deltaOperation types as well here.
163
+ // realistic app code would likely handle the other deltaOperation types as well here.
174
164
  });
175
165
  ```
176
166
 
177
- Internally, the sequence package depends on `@fluidframework/merge-tree`, and also raises `MergeTreeMaintenance` events on that tree as *maintenance* events.
167
+ Internally, the sequence package depends on `@fluidframework/merge-tree`, and also raises `MergeTreeMaintenance` events on that tree as _maintenance_ events.
178
168
  These events don't correspond directly to APIs invoked on a sequence DDS, but may be useful for advanced users.
179
169
 
180
170
  Both sequenceDelta and maintenance events are commonly used to synchronize or invalidate a view an application might have over a backing sequence DDS.
@@ -196,7 +186,7 @@ Consider a sequence like this:
196
186
  ```
197
187
 
198
188
  Now two users simultaneously insert characters at the end of the sequence. One inserts `k` and the other inserts a `c`.
199
- This is an *insert conflict*. The basic strategy for insert conflict resolution in the sequence is to merge *far*,
189
+ This is an _insert conflict_. The basic strategy for insert conflict resolution in the sequence is to merge _far_,
200
190
  closer to the end of the sequence.
201
191
 
202
192
  This merge strategy is possible because of a fundamental property of the Fluid Framework, which is guaranteed ordering.
@@ -278,53 +268,53 @@ determine the value.
278
268
 
279
269
  ## Local references
280
270
 
281
- Sequences support addition and manipulation of *local references* to locally track positions in the sequence over time.
271
+ Sequences support addition and manipulation of _local references_ to locally track positions in the sequence over time.
282
272
  As the name suggests, any created references will only exist locally; other clients will not see them.
283
273
  This can be used to implement user interactions with sequence data in a way that is robust to concurrent editing.
284
274
  For example, consider a text editor which tracks a user's cursor state.
285
275
  The application can store a local reference to the character after the cursor position:
286
276
 
287
277
  ```typescript
288
- // content: hi world!
289
- // positions: 012345678
290
- const { segment, offset } = sharedString.getContainingSegment(5)
291
- const cursor = sharedString.createLocalReferencePosition(
292
- segment,
293
- offset,
294
- ReferenceType.SlideOnRemove,
295
- /* any additional properties */ { cursorColor: 'blue' }
296
- );
297
-
298
- // cursor: x
299
- // content: hi world!
300
- // positions: 012345678
301
-
302
- // ... in some view code, retrieve the position of the local reference for rendering:
303
- const pos = sharedString.localReferencePositionToPosition(cursor); // 5
304
-
305
- // meanwhile, some other client submits an edit which gets applied to our string:
306
- otherSharedString.replaceText(1, 2, "ello");
307
-
308
- // The local sharedString state will now look like this:
309
- // cursor: x
310
- // content: hello world!
311
- // positions: 0123456789AB (hex)
312
-
313
- // ... in some view code, retrieve the position of the local reference for rendering:
314
- const pos = sharedString.localReferencePositionToPosition(cursor); // 8
278
+ // content: hi world!
279
+ // positions: 012345678
280
+ const { segment, offset } = sharedString.getContainingSegment(5);
281
+ const cursor = sharedString.createLocalReferencePosition(
282
+ segment,
283
+ offset,
284
+ ReferenceType.SlideOnRemove,
285
+ /* any additional properties */ { cursorColor: "blue" },
286
+ );
287
+
288
+ // cursor: x
289
+ // content: hi world!
290
+ // positions: 012345678
291
+
292
+ // ... in some view code, retrieve the position of the local reference for rendering:
293
+ const pos = sharedString.localReferencePositionToPosition(cursor); // 5
294
+
295
+ // meanwhile, some other client submits an edit which gets applied to our string:
296
+ otherSharedString.replaceText(1, 2, "ello");
297
+
298
+ // The local sharedString state will now look like this:
299
+ // cursor: x
300
+ // content: hello world!
301
+ // positions: 0123456789AB (hex)
302
+
303
+ // ... in some view code, retrieve the position of the local reference for rendering:
304
+ const pos = sharedString.localReferencePositionToPosition(cursor); // 8
315
305
  ```
316
306
 
317
307
  Notice that even though another client concurrently edited the string, the local reference representing the cursor is still in the correct location with no further work for the API consumer.
318
308
  The `ReferenceType.SlideOnRemove` parameter changes what happens when the segment that reference is associated with is removed.
319
- `SlideOnRemove` instructs the sequence to attempt to *slide* the reference to the start of the next furthest segment, or if no such segment exists (i.e. the end of the string has been removed), the end of the next nearest one.
309
+ `SlideOnRemove` instructs the sequence to attempt to _slide_ the reference to the start of the next furthest segment, or if no such segment exists (i.e. the end of the string has been removed), the end of the next nearest one.
320
310
 
321
311
  The [webflow](https://github.com/microsoft/FluidFramework/blob/main/examples/data-objects/webflow/src/editor/caret.ts) example demonstrates this idea in more detail.
322
312
 
323
- Unlike segments, it *is* safe to persist local references in auxiliary data structures, such as an undo-redo stack.
313
+ Unlike segments, it _is_ safe to persist local references in auxiliary data structures, such as an undo-redo stack.
324
314
 
325
315
  ## Interval collections
326
316
 
327
- Sequences support creation of *interval collections*, an auxiliary collection of intervals associated with positions in the sequence.
317
+ Sequences support creation of _interval collections_, an auxiliary collection of intervals associated with positions in the sequence.
328
318
  Like segments, intervals support adding arbitrary properties, including handles (references) to other DDSes.
329
319
  The interval collection implementation uses local references, and so benefits from all of the robustness to concurrent editing
330
320
  described in the previous section.
@@ -332,47 +322,46 @@ Unlike local references, operations on interval collections are sent to all clie
332
322
  This makes them suitable for implementing features like comment threads on a text-based documents.
333
323
  The following example illustrates these properties and highlights the major APIs supported by IntervalCollection.
334
324
 
335
-
336
325
  ```typescript
337
- // content: hi world!
338
- // positions: 012345678
339
-
340
- const comments = sharedString.getIntervalCollection("comments");
341
- const comment = comments.add(
342
- 3,
343
- 7, // (inclusive range): references "world"
344
- IntervalType.SlideOnRemove,
345
- {
346
- creator: 'my-user-id',
347
- handle: myCommentThreadDDS.handle
348
- }
349
- );
350
- // content: hi world!
351
- // positions: 012345678
352
- // comment: [ ]
353
-
354
- // Interval collection supports iterating over all intervals via Symbol.iterator or `.map()`:
355
- const allIntervalsInCollection = Array.from(comments);
356
- const allProperties = comments.map((comment) => comment.properties);
357
- // or iterating over intervals overlapping a region:
358
- const intervalsOverlappingFirstHalf = comments.findOverlappingIntervals(0, 4);
359
-
360
- // Interval endpoints are LocalReferencePositions, so all APIs in the above section can be used:
361
- const startPosition = sharedString.localReferencePositionToPosition(comment.start);
362
- const endPosition = sharedString.localReferencePositionToPosition(comment.end);
363
-
364
- // Intervals can be modified:
365
- comments.change(comment.getIntervalId(), 0, 1);
366
- // content: hi world!
367
- // positions: 012345678
368
- // comment: []
369
-
370
- // their properties can be changed:
371
- comments.changeProperties(comment.getIntervalId(), { status: "resolved" });
372
- // comment.properties === { creator: 'my-user-id', handle: <some DDS handle object>, status: "resolved" }
373
-
374
- // and they can be removed:
375
- comments.removeIntervalById(comment.getIntervalId());
326
+ // content: hi world!
327
+ // positions: 012345678
328
+
329
+ const comments = sharedString.getIntervalCollection("comments");
330
+ const comment = comments.add(
331
+ 3,
332
+ 7, // (inclusive range): references "world"
333
+ IntervalType.SlideOnRemove,
334
+ {
335
+ creator: "my-user-id",
336
+ handle: myCommentThreadDDS.handle,
337
+ },
338
+ );
339
+ // content: hi world!
340
+ // positions: 012345678
341
+ // comment: [ ]
342
+
343
+ // Interval collection supports iterating over all intervals via Symbol.iterator or `.map()`:
344
+ const allIntervalsInCollection = Array.from(comments);
345
+ const allProperties = comments.map((comment) => comment.properties);
346
+ // or iterating over intervals overlapping a region:
347
+ const intervalsOverlappingFirstHalf = comments.findOverlappingIntervals(0, 4);
348
+
349
+ // Interval endpoints are LocalReferencePositions, so all APIs in the above section can be used:
350
+ const startPosition = sharedString.localReferencePositionToPosition(comment.start);
351
+ const endPosition = sharedString.localReferencePositionToPosition(comment.end);
352
+
353
+ // Intervals can be modified:
354
+ comments.change(comment.getIntervalId(), 0, 1);
355
+ // content: hi world!
356
+ // positions: 012345678
357
+ // comment: []
358
+
359
+ // their properties can be changed:
360
+ comments.changeProperties(comment.getIntervalId(), { status: "resolved" });
361
+ // comment.properties === { creator: 'my-user-id', handle: <some DDS handle object>, status: "resolved" }
362
+
363
+ // and they can be removed:
364
+ comments.removeIntervalById(comment.getIntervalId());
376
365
  ```
377
366
 
378
367
  ## SharedString
@@ -395,22 +384,23 @@ When comparing two positions the nearer positions is closer to 0, and the farthe
395
384
 
396
385
  ### Intervals vs. markers
397
386
 
398
- Interval endpoints and markers both implement *ReferencePosition* and seem to serve a similar function so it's not obvious how they differ and why you would choose one or the other.
387
+ Interval endpoints and markers both implement _ReferencePosition_ and seem to serve a similar function so it's not obvious how they differ and why you would choose one or the other.
399
388
 
400
389
  Using the interval collection API has two main benefits:
401
390
 
402
391
  1. Efficient spatial querying
392
+
403
393
  - Interval collections support iterating all intervals overlapping the region `[start, end]` in `O(log N) + O(overlap size)` time, where `N` is the total number of intervals in the collection.
404
- This may be critical for applications that display only a small view of the document contents.
405
- On the other hand, using markers to implement intervals would require a linear scan from the start or end of the sequence to determine which intervals overlap.
394
+ This may be critical for applications that display only a small view of the document contents.
395
+ On the other hand, using markers to implement intervals would require a linear scan from the start or end of the sequence to determine which intervals overlap.
406
396
 
407
397
  2. More ergonomic modification APIs
408
398
  - Interval collections natively support a modify operation on the intervals, which allows moving the endpoints of the interval to a different place in the sequence.
409
- This operation is atomic, whereas with markers one would have to submit a delete operation for the existing position and an insert for the new one.
410
- In order to achieve the same atomicity, those operations would need to leverage the `SharedSegmentSequence.groupOperation` API,
411
- which is less user-friendly.
412
- If the ops were submitted using standard insert and delete APIs instead, there would be some potential for data loss if the delete
413
- operation ended up acknowledged by the server but the insert operation did not.
399
+ This operation is atomic, whereas with markers one would have to submit a delete operation for the existing position and an insert for the new one.
400
+ In order to achieve the same atomicity, those operations would need to leverage the `SharedSegmentSequence.groupOperation` API,
401
+ which is less user-friendly.
402
+ If the ops were submitted using standard insert and delete APIs instead, there would be some potential for data loss if the delete
403
+ operation ended up acknowledged by the server but the insert operation did not.
414
404
 
415
405
  ### Attribution
416
406
 
@@ -418,10 +408,23 @@ Using the interval collection API has two main benefits:
418
408
 
419
409
  SharedString supports storing information attributing each character position to the user who inserted it and the time at which that insertion happened.
420
410
  This functionality is off by default.
421
- Enable it by setting `{ attribution: { track: true } }` on the `IFluidDataStoreRuntime` options used to initialize the SharedString.
422
- If the config flag `"Fluid.Attribution.EnableOnNewFile"` is set, its value will override the runtime options one.
411
+ To enable this functionality, first ensure that all clients are created with an attribution policy factory in the loader settings:
423
412
 
424
- Attribution information is stored on the `attribution` field of the SharedString's segments.
413
+ ```typescript
414
+ import { createInsertOnlyAttributionPolicy } from "@fluidframework/merge-tree";
415
+ // Use these options in the IContainerContext used to instantiate your container runtime.
416
+ const options: ILoaderOptions = {
417
+ attribution: {
418
+ policyFactory: createInsertOnlyAttributionPolicy,
419
+ },
420
+ };
421
+ ```
422
+
423
+ This ensures that the client is able to load existing documents containing attribution information,
424
+ and specifies which kinds of operations should be attributed at the SharedString level (currently, only insertions).
425
+ The stored attribution information can be found on the `attribution` field of the SharedString's segments.
426
+
427
+ Next, enable the `"Fluid.Attribution.EnableOnNewFile"` config flag to start tracking attribution information for new files.
425
428
 
426
429
  ```typescript
427
430
  const { segment, offset } = sharedString.getContainingSegment(5);
@@ -430,19 +433,25 @@ const key = segment.attribution.getAtOffset(offset);
430
433
  // See the @fluidframework/attributor package for more details.
431
434
  ```
432
435
 
436
+ For further reading on attribution, see the [@fluidframework/attributor README](../../framework/attributor/README.md).
437
+
438
+ There are plans to make attribution policies more flexible, for example tracking attribution of property changes separately from segment insertion.
439
+
433
440
  ### Examples
434
441
 
435
- - Rich Text Editor Implementations
436
- - [webflow](https://github.com/microsoft/FluidFramework/tree/main/examples/data-objects/webflow)
437
- - [flowView](https://github.com/microsoft/FluidFramework/blob/main/examples/data-objects/shared-text/src/client-ui-lib/controls/flowView.ts)
442
+ - Rich Text Editor Implementations
443
+
444
+ - [webflow](https://github.com/microsoft/FluidFramework/tree/main/examples/data-objects/webflow)
445
+ - [flowView](https://github.com/microsoft/FluidFramework/blob/main/examples/data-objects/shared-text/src/client-ui-lib/controls/flowView.ts)
446
+
447
+ - Integrations with Open Source Rich Text Editors
438
448
 
439
- - Integrations with Open Source Rich Text Editors
440
- - [prosemirror](https://github.com/microsoft/FluidFramework/tree/main/examples/data-objects/prosemirror)
441
- - [smde](https://github.com/microsoft/FluidFramework/tree/main/examples/data-objects/smde)
449
+ - [prosemirror](https://github.com/microsoft/FluidFramework/tree/main/examples/data-objects/prosemirror)
450
+ - [smde](https://github.com/microsoft/FluidFramework/tree/main/examples/data-objects/smde)
442
451
 
443
- - Plain Text Editor Implementations
444
- - [collaborativeTextArea](https://github.com/microsoft/FluidFramework/blob/main/experimental/framework/react-inputs/src/CollaborativeTextArea.tsx)
445
- - [collaborativeInput](https://github.com/microsoft/FluidFramework/blob/main/experimental/framework/react-inputs/src/CollaborativeInput.tsx)
452
+ - Plain Text Editor Implementations
453
+ - [collaborativeTextArea](https://github.com/microsoft/FluidFramework/blob/main/experimental/framework/react-inputs/src/CollaborativeTextArea.tsx)
454
+ - [collaborativeInput](https://github.com/microsoft/FluidFramework/blob/main/experimental/framework/react-inputs/src/CollaborativeInput.tsx)
446
455
 
447
- [SharedMap]: https://fluidframework.com/docs/data-structures/map/
448
- [SharedString]: https://github.com/microsoft/FluidFramework/blob/main/packages/dds/sequence/src/sharedString.ts
456
+ [sharedmap]: https://fluidframework.com/docs/data-structures/map/
457
+ [sharedstring]: https://github.com/microsoft/FluidFramework/blob/main/packages/dds/sequence/src/sharedString.ts
@@ -1,4 +1,4 @@
1
1
  {
2
- "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
3
- "extends": "@fluidframework/build-common/api-extractor-common-strict.json"
2
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
3
+ "extends": "@fluidframework/build-common/api-extractor-common-strict.json"
4
4
  }
@@ -1 +1 @@
1
- {"version":3,"file":"defaultMap.d.ts","sourceRoot":"","sources":["../src/defaultMap.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"defaultMap.d.ts","sourceRoot":"","sources":["../src/defaultMap.ts"],"names":[],"mappings":"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"}
@@ -153,8 +153,8 @@ class DefaultMap {
153
153
  for (const [key, serializable] of Object.entries(json)) {
154
154
  // Back-compat: legacy documents may have handles to an intervalCollection map kernel.
155
155
  // These collections should be empty, and ValueTypes are no longer supported.
156
- if (serializable.type === shared_object_base_1.ValueType[shared_object_base_1.ValueType.Plain]
157
- || serializable.type === shared_object_base_1.ValueType[shared_object_base_1.ValueType.Shared]) {
156
+ if (serializable.type === shared_object_base_1.ValueType[shared_object_base_1.ValueType.Plain] ||
157
+ serializable.type === shared_object_base_1.ValueType[shared_object_base_1.ValueType.Shared]) {
158
158
  continue;
159
159
  }
160
160
  // Back-compat: Sequence previously arbitrarily prefixed all interval collection keys with
@@ -238,7 +238,8 @@ class DefaultMap {
238
238
  * @returns The local value that was produced
239
239
  */
240
240
  makeLocal(key, serializable) {
241
- (0, common_utils_1.assert)(serializable.type !== shared_object_base_1.ValueType[shared_object_base_1.ValueType.Plain] && serializable.type !== shared_object_base_1.ValueType[shared_object_base_1.ValueType.Shared], 0x2e1 /* "Support for plain value types removed." */);
241
+ (0, common_utils_1.assert)(serializable.type !== shared_object_base_1.ValueType[shared_object_base_1.ValueType.Plain] &&
242
+ serializable.type !== shared_object_base_1.ValueType[shared_object_base_1.ValueType.Shared], 0x2e1 /* "Support for plain value types removed." */);
242
243
  serializable.value = (0, shared_object_base_1.parseHandles)(serializable.value, this.serializer);
243
244
  const localValue = this.type.factory.load(this.makeMapValueOpEmitter(key), serializable.value);
244
245
  return new localValues_1.ValueTypeLocalValue(localValue, this.type);
@@ -271,7 +272,7 @@ class DefaultMap {
271
272
  const localValue = this.data.get(op.key);
272
273
  (0, common_utils_1.assert)(localValue !== undefined, 0x3f8 /* Local value expected on resubmission */);
273
274
  const handler = localValue.getOpHandler(op.value.opName);
274
- const { rebasedOp, rebasedLocalOpMetadata, } = handler.rebase(localValue.value, op.value, localOpMetadata);
275
+ const { rebasedOp, rebasedLocalOpMetadata } = handler.rebase(localValue.value, op.value, localOpMetadata);
275
276
  this.submitMessage(Object.assign(Object.assign({}, op), { value: rebasedOp }), rebasedLocalOpMetadata);
276
277
  },
277
278
  getStashedOpLocalMetadata: (op) => {