@fluidframework/sequence 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.224419
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +9 -11
- package/.mocharc.js +12 -0
- package/CHANGELOG.md +449 -0
- package/README.md +364 -183
- package/api-extractor-lint.json +4 -0
- package/api-extractor.json +2 -2
- package/api-report/sequence.api.md +741 -0
- package/dist/{defaultMap.js → defaultMap.cjs} +29 -22
- package/dist/defaultMap.cjs.map +1 -0
- package/dist/defaultMap.d.ts +7 -6
- package/dist/defaultMap.d.ts.map +1 -1
- package/dist/defaultMapInterfaces.cjs +7 -0
- package/dist/defaultMapInterfaces.cjs.map +1 -0
- package/dist/defaultMapInterfaces.d.ts +44 -12
- package/dist/defaultMapInterfaces.d.ts.map +1 -1
- package/dist/index.cjs +60 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +14 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/intervalCollection.cjs +1159 -0
- package/dist/intervalCollection.cjs.map +1 -0
- package/dist/intervalCollection.d.ts +461 -162
- package/dist/intervalCollection.d.ts.map +1 -1
- package/dist/intervalIndex/endpointInRangeIndex.cjs +66 -0
- package/dist/intervalIndex/endpointInRangeIndex.cjs.map +1 -0
- package/dist/intervalIndex/endpointInRangeIndex.d.ts +34 -0
- package/dist/intervalIndex/endpointInRangeIndex.d.ts.map +1 -0
- package/dist/intervalIndex/endpointIndex.cjs +47 -0
- package/dist/intervalIndex/endpointIndex.cjs.map +1 -0
- package/dist/intervalIndex/endpointIndex.d.ts +38 -0
- package/dist/intervalIndex/endpointIndex.d.ts.map +1 -0
- package/dist/intervalIndex/idIntervalIndex.cjs +44 -0
- package/dist/intervalIndex/idIntervalIndex.cjs.map +1 -0
- package/dist/intervalIndex/idIntervalIndex.d.ts +18 -0
- package/dist/intervalIndex/idIntervalIndex.d.ts.map +1 -0
- package/dist/intervalIndex/index.cjs +24 -0
- package/dist/intervalIndex/index.cjs.map +1 -0
- package/dist/intervalIndex/index.d.ts +13 -0
- package/dist/intervalIndex/index.d.ts.map +1 -0
- package/dist/{defaultMapInterfaces.js → intervalIndex/intervalIndex.cjs} +1 -1
- package/dist/intervalIndex/intervalIndex.cjs.map +1 -0
- package/dist/intervalIndex/intervalIndex.d.ts +30 -0
- package/dist/intervalIndex/intervalIndex.d.ts.map +1 -0
- package/dist/intervalIndex/intervalIndexUtils.cjs +22 -0
- package/dist/intervalIndex/intervalIndexUtils.cjs.map +1 -0
- package/dist/intervalIndex/intervalIndexUtils.d.ts +17 -0
- package/dist/intervalIndex/intervalIndexUtils.d.ts.map +1 -0
- package/dist/intervalIndex/overlappingIntervalsIndex.cjs +116 -0
- package/dist/intervalIndex/overlappingIntervalsIndex.cjs.map +1 -0
- package/dist/intervalIndex/overlappingIntervalsIndex.d.ts +44 -0
- package/dist/intervalIndex/overlappingIntervalsIndex.d.ts.map +1 -0
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.cjs +41 -0
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.cjs.map +1 -0
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts +11 -0
- package/dist/intervalIndex/overlappingSequenceIntervalsIndex.d.ts.map +1 -0
- package/dist/intervalIndex/sequenceIntervalIndexes.cjs +7 -0
- package/dist/intervalIndex/sequenceIntervalIndexes.cjs.map +1 -0
- package/dist/intervalIndex/sequenceIntervalIndexes.d.ts +35 -0
- package/dist/intervalIndex/sequenceIntervalIndexes.d.ts.map +1 -0
- package/dist/intervalIndex/startpointInRangeIndex.cjs +66 -0
- package/dist/intervalIndex/startpointInRangeIndex.cjs.map +1 -0
- package/dist/intervalIndex/startpointInRangeIndex.d.ts +34 -0
- package/dist/intervalIndex/startpointInRangeIndex.d.ts.map +1 -0
- package/dist/intervalTree.cjs +80 -0
- package/dist/intervalTree.cjs.map +1 -0
- package/dist/intervalTree.d.ts +24 -0
- package/dist/intervalTree.d.ts.map +1 -0
- package/dist/intervals/index.cjs +23 -0
- package/dist/intervals/index.cjs.map +1 -0
- package/dist/intervals/index.d.ts +8 -0
- package/dist/intervals/index.d.ts.map +1 -0
- package/dist/intervals/interval.cjs +181 -0
- package/dist/intervals/interval.cjs.map +1 -0
- package/dist/intervals/interval.d.ts +84 -0
- package/dist/intervals/interval.d.ts.map +1 -0
- package/dist/intervals/intervalUtils.cjs +83 -0
- package/dist/intervals/intervalUtils.cjs.map +1 -0
- package/dist/intervals/intervalUtils.d.ts +230 -0
- package/dist/intervals/intervalUtils.d.ts.map +1 -0
- package/dist/intervals/sequenceInterval.cjs +378 -0
- package/dist/intervals/sequenceInterval.cjs.map +1 -0
- package/dist/intervals/sequenceInterval.d.ts +137 -0
- package/dist/intervals/sequenceInterval.d.ts.map +1 -0
- package/dist/{localValues.js → localValues.cjs} +1 -1
- package/dist/localValues.cjs.map +1 -0
- package/dist/localValues.d.ts +2 -1
- package/dist/localValues.d.ts.map +1 -1
- package/dist/{packageVersion.js → packageVersion.cjs} +2 -2
- package/dist/packageVersion.cjs.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/revertibles.cjs +425 -0
- package/dist/revertibles.cjs.map +1 -0
- package/dist/revertibles.d.ts +86 -0
- package/dist/revertibles.d.ts.map +1 -0
- package/dist/sequence-alpha.d.ts +1315 -0
- package/dist/sequence-beta.d.ts +244 -0
- package/dist/sequence-public.d.ts +244 -0
- package/dist/sequence-untrimmed.d.ts +1803 -0
- package/dist/{sequence.js → sequence.cjs} +226 -156
- package/dist/sequence.cjs.map +1 -0
- package/dist/sequence.d.ts +125 -48
- package/dist/sequence.d.ts.map +1 -1
- package/dist/{sequenceDeltaEvent.js → sequenceDeltaEvent.cjs} +18 -8
- package/dist/sequenceDeltaEvent.cjs.map +1 -0
- package/dist/sequenceDeltaEvent.d.ts +24 -7
- package/dist/sequenceDeltaEvent.d.ts.map +1 -1
- package/dist/sequenceFactory.cjs +55 -0
- package/dist/sequenceFactory.cjs.map +1 -0
- package/dist/sequenceFactory.d.ts +3 -89
- package/dist/sequenceFactory.d.ts.map +1 -1
- package/dist/{sharedIntervalCollection.js → sharedIntervalCollection.cjs} +17 -22
- package/dist/sharedIntervalCollection.cjs.map +1 -0
- package/dist/sharedIntervalCollection.d.ts +12 -12
- package/dist/sharedIntervalCollection.d.ts.map +1 -1
- package/dist/{sharedSequence.js → sharedSequence.cjs} +29 -22
- package/dist/sharedSequence.cjs.map +1 -0
- package/dist/sharedSequence.d.ts +14 -2
- package/dist/sharedSequence.d.ts.map +1 -1
- package/dist/sharedString.cjs +286 -0
- package/dist/sharedString.cjs.map +1 -0
- package/dist/sharedString.d.ts +58 -22
- package/dist/sharedString.d.ts.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/lib/{defaultMap.d.ts → defaultMap.d.mts} +7 -6
- package/lib/defaultMap.d.mts.map +1 -0
- package/lib/{defaultMap.js → defaultMap.mjs} +28 -21
- package/lib/defaultMap.mjs.map +1 -0
- package/lib/{defaultMapInterfaces.d.ts → defaultMapInterfaces.d.mts} +44 -12
- package/lib/defaultMapInterfaces.d.mts.map +1 -0
- package/lib/defaultMapInterfaces.mjs +6 -0
- package/lib/defaultMapInterfaces.mjs.map +1 -0
- package/lib/index.d.mts +17 -0
- package/lib/index.d.mts.map +1 -0
- package/lib/index.mjs +16 -0
- package/lib/index.mjs.map +1 -0
- package/lib/intervalCollection.d.mts +569 -0
- package/lib/intervalCollection.d.mts.map +1 -0
- package/lib/intervalCollection.mjs +1144 -0
- package/lib/intervalCollection.mjs.map +1 -0
- package/lib/intervalIndex/endpointInRangeIndex.d.mts +34 -0
- package/lib/intervalIndex/endpointInRangeIndex.d.mts.map +1 -0
- package/lib/intervalIndex/endpointInRangeIndex.mjs +61 -0
- package/lib/intervalIndex/endpointInRangeIndex.mjs.map +1 -0
- package/lib/intervalIndex/endpointIndex.d.mts +38 -0
- package/lib/intervalIndex/endpointIndex.d.mts.map +1 -0
- package/lib/intervalIndex/endpointIndex.mjs +42 -0
- package/lib/intervalIndex/endpointIndex.mjs.map +1 -0
- package/lib/intervalIndex/idIntervalIndex.d.mts +18 -0
- package/lib/intervalIndex/idIntervalIndex.d.mts.map +1 -0
- package/lib/intervalIndex/idIntervalIndex.mjs +40 -0
- package/lib/intervalIndex/idIntervalIndex.mjs.map +1 -0
- package/lib/intervalIndex/index.d.mts +13 -0
- package/lib/intervalIndex/index.d.mts.map +1 -0
- package/lib/intervalIndex/index.mjs +11 -0
- package/lib/intervalIndex/index.mjs.map +1 -0
- package/lib/intervalIndex/intervalIndex.d.mts +30 -0
- package/lib/intervalIndex/intervalIndex.d.mts.map +1 -0
- package/lib/{defaultMapInterfaces.js → intervalIndex/intervalIndex.mjs} +1 -1
- package/lib/intervalIndex/intervalIndex.mjs.map +1 -0
- package/lib/intervalIndex/intervalIndexUtils.d.mts +17 -0
- package/lib/intervalIndex/intervalIndexUtils.d.mts.map +1 -0
- package/lib/intervalIndex/intervalIndexUtils.mjs +18 -0
- package/lib/intervalIndex/intervalIndexUtils.mjs.map +1 -0
- package/lib/intervalIndex/overlappingIntervalsIndex.d.mts +44 -0
- package/lib/intervalIndex/overlappingIntervalsIndex.d.mts.map +1 -0
- package/lib/intervalIndex/overlappingIntervalsIndex.mjs +111 -0
- package/lib/intervalIndex/overlappingIntervalsIndex.mjs.map +1 -0
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.mts +11 -0
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.d.mts.map +1 -0
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.mjs +37 -0
- package/lib/intervalIndex/overlappingSequenceIntervalsIndex.mjs.map +1 -0
- package/lib/intervalIndex/sequenceIntervalIndexes.d.mts +35 -0
- package/lib/intervalIndex/sequenceIntervalIndexes.d.mts.map +1 -0
- package/lib/intervalIndex/sequenceIntervalIndexes.mjs +6 -0
- package/lib/intervalIndex/sequenceIntervalIndexes.mjs.map +1 -0
- package/lib/intervalIndex/startpointInRangeIndex.d.mts +34 -0
- package/lib/intervalIndex/startpointInRangeIndex.d.mts.map +1 -0
- package/lib/intervalIndex/startpointInRangeIndex.mjs +61 -0
- package/lib/intervalIndex/startpointInRangeIndex.mjs.map +1 -0
- package/lib/intervalTree.d.mts +24 -0
- package/lib/intervalTree.d.mts.map +1 -0
- package/lib/intervalTree.mjs +76 -0
- package/lib/intervalTree.mjs.map +1 -0
- package/lib/intervals/index.d.mts +8 -0
- package/lib/intervals/index.d.mts.map +1 -0
- package/lib/intervals/index.mjs +8 -0
- package/lib/intervals/index.mjs.map +1 -0
- package/lib/intervals/interval.d.mts +84 -0
- package/lib/intervals/interval.d.mts.map +1 -0
- package/lib/intervals/interval.mjs +176 -0
- package/lib/intervals/interval.mjs.map +1 -0
- package/lib/intervals/intervalUtils.d.mts +230 -0
- package/lib/intervals/intervalUtils.d.mts.map +1 -0
- package/lib/intervals/intervalUtils.mjs +77 -0
- package/lib/intervals/intervalUtils.mjs.map +1 -0
- package/lib/intervals/sequenceInterval.d.mts +137 -0
- package/lib/intervals/sequenceInterval.d.mts.map +1 -0
- package/lib/intervals/sequenceInterval.mjs +370 -0
- package/lib/intervals/sequenceInterval.mjs.map +1 -0
- package/lib/{localValues.d.ts → localValues.d.mts} +3 -2
- package/lib/localValues.d.mts.map +1 -0
- package/lib/{localValues.js → localValues.mjs} +2 -2
- package/lib/localValues.mjs.map +1 -0
- package/lib/{packageVersion.d.ts → packageVersion.d.mts} +1 -1
- package/lib/{packageVersion.d.ts.map → packageVersion.d.mts.map} +1 -1
- package/lib/{packageVersion.js → packageVersion.mjs} +2 -2
- package/lib/packageVersion.mjs.map +1 -0
- package/lib/revertibles.d.mts +86 -0
- package/lib/revertibles.d.mts.map +1 -0
- package/lib/revertibles.mjs +416 -0
- package/lib/revertibles.mjs.map +1 -0
- package/lib/sequence-alpha.d.mts +1315 -0
- package/lib/sequence-beta.d.mts +244 -0
- package/lib/sequence-public.d.mts +244 -0
- package/lib/sequence-untrimmed.d.mts +1803 -0
- package/lib/{sequence.d.ts → sequence.d.mts} +127 -50
- package/lib/sequence.d.mts.map +1 -0
- package/lib/{sequence.js → sequence.mjs} +225 -152
- package/lib/sequence.mjs.map +1 -0
- package/lib/{sequenceDeltaEvent.d.ts → sequenceDeltaEvent.d.mts} +24 -7
- package/lib/sequenceDeltaEvent.d.mts.map +1 -0
- package/lib/{sequenceDeltaEvent.js → sequenceDeltaEvent.mjs} +20 -8
- package/lib/sequenceDeltaEvent.mjs.map +1 -0
- package/lib/sequenceFactory.d.mts +22 -0
- package/lib/sequenceFactory.d.mts.map +1 -0
- package/lib/sequenceFactory.mjs +51 -0
- package/lib/sequenceFactory.mjs.map +1 -0
- package/lib/{sharedIntervalCollection.d.ts → sharedIntervalCollection.d.mts} +12 -12
- package/lib/sharedIntervalCollection.d.mts.map +1 -0
- package/lib/{sharedIntervalCollection.js → sharedIntervalCollection.mjs} +16 -21
- package/lib/sharedIntervalCollection.mjs.map +1 -0
- package/lib/{sharedSequence.d.ts → sharedSequence.d.mts} +15 -3
- package/lib/sharedSequence.d.mts.map +1 -0
- package/lib/{sharedSequence.js → sharedSequence.mjs} +30 -23
- package/lib/sharedSequence.mjs.map +1 -0
- package/lib/{sharedString.d.ts → sharedString.d.mts} +60 -24
- package/lib/sharedString.d.mts.map +1 -0
- package/lib/sharedString.mjs +281 -0
- package/lib/sharedString.mjs.map +1 -0
- package/package.json +146 -75
- package/prettier.config.cjs +8 -0
- package/sequence.test-files.tar +0 -0
- package/src/defaultMap.ts +417 -403
- package/src/defaultMapInterfaces.ts +157 -117
- package/src/index.ts +86 -26
- package/src/intervalCollection.ts +2043 -1563
- package/src/intervalIndex/endpointInRangeIndex.ts +116 -0
- package/src/intervalIndex/endpointIndex.ts +91 -0
- package/src/intervalIndex/idIntervalIndex.ts +64 -0
- package/src/intervalIndex/index.ts +25 -0
- package/src/intervalIndex/intervalIndex.ts +32 -0
- package/src/intervalIndex/intervalIndexUtils.ts +27 -0
- package/src/intervalIndex/overlappingIntervalsIndex.ts +187 -0
- package/src/intervalIndex/overlappingSequenceIntervalsIndex.ts +80 -0
- package/src/intervalIndex/sequenceIntervalIndexes.ts +34 -0
- package/src/intervalIndex/startpointInRangeIndex.ts +114 -0
- package/src/intervalTree.ts +98 -0
- package/src/intervals/index.ts +25 -0
- package/src/intervals/interval.ts +238 -0
- package/src/intervals/intervalUtils.ts +288 -0
- package/src/intervals/sequenceInterval.ts +616 -0
- package/src/localValues.ts +68 -73
- package/src/packageVersion.ts +1 -1
- package/src/revertibles.ts +693 -0
- package/src/sequence.ts +845 -690
- package/src/sequenceDeltaEvent.ts +164 -131
- package/src/sequenceFactory.ts +58 -214
- package/src/sharedIntervalCollection.ts +161 -152
- package/src/sharedSequence.ts +181 -167
- package/src/sharedString.ts +390 -234
- package/tsc-multi.test.json +10 -0
- package/tsconfig.json +11 -13
- package/.editorconfig +0 -7
- package/.vscode/launch.json +0 -15
- package/dist/defaultMap.js.map +0 -1
- package/dist/defaultMapInterfaces.js.map +0 -1
- package/dist/index.js +0 -44
- package/dist/index.js.map +0 -1
- package/dist/intervalCollection.js +0 -1250
- package/dist/intervalCollection.js.map +0 -1
- package/dist/localValues.js.map +0 -1
- package/dist/packageVersion.js.map +0 -1
- package/dist/sequence.js.map +0 -1
- package/dist/sequenceDeltaEvent.js.map +0 -1
- package/dist/sequenceFactory.js +0 -192
- package/dist/sequenceFactory.js.map +0 -1
- package/dist/sharedIntervalCollection.js.map +0 -1
- package/dist/sharedNumberSequence.d.ts +0 -50
- package/dist/sharedNumberSequence.d.ts.map +0 -1
- package/dist/sharedNumberSequence.js +0 -61
- package/dist/sharedNumberSequence.js.map +0 -1
- package/dist/sharedObjectSequence.d.ts +0 -50
- package/dist/sharedObjectSequence.d.ts.map +0 -1
- package/dist/sharedObjectSequence.js +0 -61
- package/dist/sharedObjectSequence.js.map +0 -1
- package/dist/sharedSequence.js.map +0 -1
- package/dist/sharedString.js +0 -187
- package/dist/sharedString.js.map +0 -1
- package/dist/sparsematrix.d.ts +0 -139
- package/dist/sparsematrix.d.ts.map +0 -1
- package/dist/sparsematrix.js +0 -332
- package/dist/sparsematrix.js.map +0 -1
- package/lib/defaultMap.d.ts.map +0 -1
- package/lib/defaultMap.js.map +0 -1
- package/lib/defaultMapInterfaces.d.ts.map +0 -1
- package/lib/defaultMapInterfaces.js.map +0 -1
- package/lib/index.d.ts +0 -27
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -26
- package/lib/index.js.map +0 -1
- package/lib/intervalCollection.d.ts +0 -270
- package/lib/intervalCollection.d.ts.map +0 -1
- package/lib/intervalCollection.js +0 -1238
- package/lib/intervalCollection.js.map +0 -1
- package/lib/localValues.d.ts.map +0 -1
- package/lib/localValues.js.map +0 -1
- package/lib/packageVersion.js.map +0 -1
- package/lib/sequence.d.ts.map +0 -1
- package/lib/sequence.js.map +0 -1
- package/lib/sequenceDeltaEvent.d.ts.map +0 -1
- package/lib/sequenceDeltaEvent.js.map +0 -1
- package/lib/sequenceFactory.d.ts +0 -108
- package/lib/sequenceFactory.d.ts.map +0 -1
- package/lib/sequenceFactory.js +0 -186
- package/lib/sequenceFactory.js.map +0 -1
- package/lib/sharedIntervalCollection.d.ts.map +0 -1
- package/lib/sharedIntervalCollection.js.map +0 -1
- package/lib/sharedNumberSequence.d.ts +0 -50
- package/lib/sharedNumberSequence.d.ts.map +0 -1
- package/lib/sharedNumberSequence.js +0 -57
- package/lib/sharedNumberSequence.js.map +0 -1
- package/lib/sharedObjectSequence.d.ts +0 -50
- package/lib/sharedObjectSequence.d.ts.map +0 -1
- package/lib/sharedObjectSequence.js +0 -57
- package/lib/sharedObjectSequence.js.map +0 -1
- package/lib/sharedSequence.d.ts.map +0 -1
- package/lib/sharedSequence.js.map +0 -1
- package/lib/sharedString.d.ts.map +0 -1
- package/lib/sharedString.js +0 -183
- package/lib/sharedString.js.map +0 -1
- package/lib/sparsematrix.d.ts +0 -139
- package/lib/sparsematrix.d.ts.map +0 -1
- package/lib/sparsematrix.js +0 -323
- package/lib/sparsematrix.js.map +0 -1
- package/src/sharedNumberSequence.ts +0 -62
- package/src/sharedObjectSequence.ts +0 -62
- package/src/sparsematrix.ts +0 -421
- package/tsconfig.esnext.json +0 -7
package/src/sequence.ts
CHANGED
|
@@ -2,62 +2,63 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from "@fluidframework/protocol-definitions";
|
|
5
|
+
|
|
6
|
+
import { assert, Deferred } from "@fluidframework/core-utils";
|
|
7
|
+
import { bufferToString } from "@fluid-internal/client-utils";
|
|
8
|
+
import { LoggingError, createChildLogger } from "@fluidframework/telemetry-utils";
|
|
9
|
+
import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol-definitions";
|
|
11
10
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
IChannelAttributes,
|
|
12
|
+
IFluidDataStoreRuntime,
|
|
13
|
+
IChannelStorageService,
|
|
15
14
|
} from "@fluidframework/datastore-definitions";
|
|
16
15
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
16
|
+
// eslint-disable-next-line import/no-deprecated
|
|
17
|
+
Client,
|
|
18
|
+
createAnnotateRangeOp,
|
|
19
|
+
// eslint-disable-next-line import/no-deprecated
|
|
20
|
+
createGroupOp,
|
|
21
|
+
createInsertOp,
|
|
22
|
+
createRemoveRangeOp,
|
|
23
|
+
IJSONSegment,
|
|
24
|
+
IMergeTreeAnnotateMsg,
|
|
25
|
+
IMergeTreeDeltaOp,
|
|
26
|
+
IMergeTreeGroupMsg,
|
|
27
|
+
IMergeTreeOp,
|
|
28
|
+
IMergeTreeRemoveMsg,
|
|
29
|
+
IRelativePosition,
|
|
30
|
+
ISegment,
|
|
31
|
+
ISegmentAction,
|
|
32
|
+
LocalReferencePosition,
|
|
33
|
+
matchProperties,
|
|
34
|
+
MergeTreeDeltaType,
|
|
35
|
+
PropertySet,
|
|
36
|
+
ReferencePosition,
|
|
37
|
+
ReferenceType,
|
|
38
|
+
MergeTreeRevertibleDriver,
|
|
39
|
+
SegmentGroup,
|
|
40
|
+
IMergeTreeObliterateMsg,
|
|
41
|
+
createObliterateRangeOp,
|
|
42
|
+
SlidingPreference,
|
|
41
43
|
} from "@fluidframework/merge-tree";
|
|
42
44
|
import { ObjectStoragePartition, SummaryTreeBuilder } from "@fluidframework/runtime-utils";
|
|
43
45
|
import {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
SummarySerializer,
|
|
46
|
+
IFluidSerializer,
|
|
47
|
+
makeHandlesSerializable,
|
|
48
|
+
parseHandles,
|
|
49
|
+
SharedObject,
|
|
50
|
+
ISharedObjectEvents,
|
|
50
51
|
} from "@fluidframework/shared-object-base";
|
|
51
|
-
import { IEventThisPlaceHolder } from "@fluidframework/
|
|
52
|
+
import { IEventThisPlaceHolder } from "@fluidframework/core-interfaces";
|
|
52
53
|
import { ISummaryTreeWithStats, ITelemetryContext } from "@fluidframework/runtime-definitions";
|
|
53
|
-
|
|
54
|
+
import { DefaultMap, IMapOperation } from "./defaultMap";
|
|
55
|
+
import { IMapMessageLocalMetadata, IValueChanged } from "./defaultMapInterfaces";
|
|
56
|
+
import { SequenceInterval } from "./intervals";
|
|
54
57
|
import {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
IIntervalCollection,
|
|
59
|
+
IntervalCollection,
|
|
60
|
+
SequenceIntervalCollectionValueType,
|
|
58
61
|
} from "./intervalCollection";
|
|
59
|
-
import { DefaultMap } from "./defaultMap";
|
|
60
|
-
import { IMapMessageLocalMetadata, IValueChanged } from "./defaultMapInterfaces";
|
|
61
62
|
import { SequenceDeltaEvent, SequenceMaintenanceEvent } from "./sequenceDeltaEvent";
|
|
62
63
|
import { ISharedIntervalCollection } from "./sharedIntervalCollection";
|
|
63
64
|
|
|
@@ -67,7 +68,7 @@ const contentPath = "content";
|
|
|
67
68
|
/**
|
|
68
69
|
* Events emitted in response to changes to the sequence data.
|
|
69
70
|
*
|
|
70
|
-
*
|
|
71
|
+
* @remarks
|
|
71
72
|
*
|
|
72
73
|
* The following is the list of events emitted.
|
|
73
74
|
*
|
|
@@ -96,652 +97,806 @@ const contentPath = "content";
|
|
|
96
97
|
* - `event` - Various information on the segments that were modified.
|
|
97
98
|
*
|
|
98
99
|
* - `target` - The sequence itself.
|
|
100
|
+
* @alpha
|
|
99
101
|
*/
|
|
100
102
|
export interface ISharedSegmentSequenceEvents extends ISharedObjectEvents {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
(
|
|
104
|
+
event: "createIntervalCollection",
|
|
105
|
+
listener: (label: string, local: boolean, target: IEventThisPlaceHolder) => void,
|
|
106
|
+
): void;
|
|
107
|
+
(
|
|
108
|
+
event: "sequenceDelta",
|
|
109
|
+
listener: (event: SequenceDeltaEvent, target: IEventThisPlaceHolder) => void,
|
|
110
|
+
): void;
|
|
111
|
+
(
|
|
112
|
+
event: "maintenance",
|
|
113
|
+
listener: (event: SequenceMaintenanceEvent, target: IEventThisPlaceHolder) => void,
|
|
114
|
+
): void;
|
|
106
115
|
}
|
|
107
116
|
|
|
117
|
+
/**
|
|
118
|
+
* @alpha
|
|
119
|
+
*/
|
|
108
120
|
export abstract class SharedSegmentSequence<T extends ISegment>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
121
|
+
extends SharedObject<ISharedSegmentSequenceEvents>
|
|
122
|
+
implements ISharedIntervalCollection<SequenceInterval>, MergeTreeRevertibleDriver
|
|
123
|
+
{
|
|
124
|
+
get loaded(): Promise<void> {
|
|
125
|
+
return this.loadedDeferred.promise;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* This is a safeguard to avoid problematic reentrancy of local ops. This type of scenario occurs if the user of SharedString subscribes
|
|
130
|
+
* to the `sequenceDelta` event and uses the callback for a local op to submit further local ops.
|
|
131
|
+
* Historically (before 2.0.0-internal.6.1.0), doing so would result in eventual consistency issues or a corrupted document.
|
|
132
|
+
* 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,
|
|
133
|
+
* but there is still little test coverage for reentrant scenarios.
|
|
134
|
+
* Additionally, applications submitting ops from inside change events need to take extreme care that their data models also support reentrancy.
|
|
135
|
+
* Since this is likely not the case, by default SharedString throws when encountering reentrant ops.
|
|
136
|
+
*
|
|
137
|
+
* An application using SharedString which explicitly wants to opt in to allowing reentrancy anyway can set `sharedStringPreventReentrancy`
|
|
138
|
+
* on the data store options to `false`.
|
|
139
|
+
*/
|
|
140
|
+
protected guardReentrancy: <TRet>(callback: () => TRet) => TRet;
|
|
141
|
+
|
|
142
|
+
private static createOpsFromDelta(event: SequenceDeltaEvent): IMergeTreeDeltaOp[] {
|
|
143
|
+
const ops: IMergeTreeDeltaOp[] = [];
|
|
144
|
+
for (const r of event.ranges) {
|
|
145
|
+
switch (event.deltaOperation) {
|
|
146
|
+
case MergeTreeDeltaType.ANNOTATE: {
|
|
147
|
+
const lastAnnotate = ops[ops.length - 1] as IMergeTreeAnnotateMsg;
|
|
148
|
+
const props: PropertySet = {};
|
|
149
|
+
for (const key of Object.keys(r.propertyDeltas)) {
|
|
150
|
+
props[key] = r.segment.properties?.[key] ?? null;
|
|
151
|
+
}
|
|
152
|
+
if (
|
|
153
|
+
lastAnnotate &&
|
|
154
|
+
lastAnnotate.pos2 === r.position &&
|
|
155
|
+
matchProperties(lastAnnotate.props, props)
|
|
156
|
+
) {
|
|
157
|
+
lastAnnotate.pos2 += r.segment.cachedLength;
|
|
158
|
+
} else {
|
|
159
|
+
ops.push(
|
|
160
|
+
createAnnotateRangeOp(
|
|
161
|
+
r.position,
|
|
162
|
+
r.position + r.segment.cachedLength,
|
|
163
|
+
props,
|
|
164
|
+
),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
case MergeTreeDeltaType.INSERT:
|
|
171
|
+
ops.push(createInsertOp(r.position, r.segment.clone().toJSONObject()));
|
|
172
|
+
break;
|
|
173
|
+
|
|
174
|
+
case MergeTreeDeltaType.REMOVE: {
|
|
175
|
+
const lastRem = ops[ops.length - 1] as IMergeTreeRemoveMsg;
|
|
176
|
+
if (lastRem?.pos1 === r.position) {
|
|
177
|
+
assert(
|
|
178
|
+
lastRem.pos2 !== undefined,
|
|
179
|
+
0x3ff /* pos2 should not be undefined here */,
|
|
180
|
+
);
|
|
181
|
+
lastRem.pos2 += r.segment.cachedLength;
|
|
182
|
+
} else {
|
|
183
|
+
ops.push(
|
|
184
|
+
createRemoveRangeOp(r.position, r.position + r.segment.cachedLength),
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
case MergeTreeDeltaType.OBLITERATE: {
|
|
191
|
+
const lastRem = ops[ops.length - 1] as IMergeTreeObliterateMsg;
|
|
192
|
+
if (lastRem?.pos1 === r.position) {
|
|
193
|
+
assert(
|
|
194
|
+
lastRem.pos2 !== undefined,
|
|
195
|
+
0x874 /* pos2 should not be undefined here */,
|
|
196
|
+
);
|
|
197
|
+
lastRem.pos2 += r.segment.cachedLength;
|
|
198
|
+
} else {
|
|
199
|
+
ops.push(
|
|
200
|
+
createObliterateRangeOp(
|
|
201
|
+
r.position,
|
|
202
|
+
r.position + r.segment.cachedLength,
|
|
203
|
+
),
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
default:
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return ops;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// eslint-disable-next-line import/no-deprecated
|
|
216
|
+
protected client: Client;
|
|
217
|
+
/** `Deferred` that triggers once the object is loaded */
|
|
218
|
+
protected loadedDeferred = new Deferred<void>();
|
|
219
|
+
// cache out going ops created when partial loading
|
|
220
|
+
private readonly loadedDeferredOutgoingOps: [IMergeTreeOp, SegmentGroup | SegmentGroup[]][] =
|
|
221
|
+
[];
|
|
222
|
+
// cache incoming ops that arrive when partial loading
|
|
223
|
+
private deferIncomingOps = true;
|
|
224
|
+
private readonly loadedDeferredIncomingOps: ISequencedDocumentMessage[] = [];
|
|
225
|
+
|
|
226
|
+
private messagesSinceMSNChange: ISequencedDocumentMessage[] = [];
|
|
227
|
+
private readonly intervalCollections: DefaultMap<IntervalCollection<SequenceInterval>>;
|
|
228
|
+
constructor(
|
|
229
|
+
private readonly dataStoreRuntime: IFluidDataStoreRuntime,
|
|
230
|
+
public id: string,
|
|
231
|
+
attributes: IChannelAttributes,
|
|
232
|
+
public readonly segmentFromSpec: (spec: IJSONSegment) => ISegment,
|
|
233
|
+
) {
|
|
234
|
+
super(id, dataStoreRuntime, attributes, "fluid_sequence_");
|
|
235
|
+
|
|
236
|
+
this.guardReentrancy =
|
|
237
|
+
dataStoreRuntime.options.sharedStringPreventReentrancy ?? true
|
|
238
|
+
? ensureNoReentrancy
|
|
239
|
+
: createReentrancyDetector((depth) => {
|
|
240
|
+
if (totalReentrancyLogs > 0) {
|
|
241
|
+
totalReentrancyLogs--;
|
|
242
|
+
this.logger.sendTelemetryEvent(
|
|
243
|
+
{ eventName: "LocalOpReentry", depth },
|
|
244
|
+
new LoggingError(reentrancyErrorMessage),
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
this.loadedDeferred.promise.catch((error) => {
|
|
250
|
+
this.logger.sendErrorEvent({ eventName: "SequenceLoadFailed" }, error);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// eslint-disable-next-line import/no-deprecated
|
|
254
|
+
this.client = new Client(
|
|
255
|
+
segmentFromSpec,
|
|
256
|
+
createChildLogger({
|
|
257
|
+
logger: this.logger,
|
|
258
|
+
namespace: "SharedSegmentSequence.MergeTreeClient",
|
|
259
|
+
}),
|
|
260
|
+
dataStoreRuntime.options,
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
this.client.prependListener("delta", (opArgs, deltaArgs) => {
|
|
264
|
+
const event = new SequenceDeltaEvent(opArgs, deltaArgs, this.client);
|
|
265
|
+
if (opArgs.stashed !== true && event.isLocal) {
|
|
266
|
+
this.submitSequenceMessage(opArgs.op);
|
|
267
|
+
}
|
|
268
|
+
this.emit("sequenceDelta", event, this);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
this.client.on("maintenance", (args, opArgs) => {
|
|
272
|
+
this.emit("maintenance", new SequenceMaintenanceEvent(opArgs, args, this.client), this);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
this.intervalCollections = new DefaultMap(
|
|
276
|
+
this.serializer,
|
|
277
|
+
this.handle,
|
|
278
|
+
(op, localOpMetadata) => this.submitLocalMessage(op, localOpMetadata),
|
|
279
|
+
new SequenceIntervalCollectionValueType(),
|
|
280
|
+
dataStoreRuntime.options,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* @param start - The inclusive start of the range to remove
|
|
286
|
+
* @param end - The exclusive end of the range to remove
|
|
287
|
+
*/
|
|
288
|
+
public removeRange(start: number, end: number): void {
|
|
289
|
+
this.guardReentrancy(() => this.client.removeRangeLocal(start, end));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Obliterate is similar to remove, but differs in that segments concurrently
|
|
294
|
+
* inserted into an obliterated range will also be removed
|
|
295
|
+
*
|
|
296
|
+
* @param start - The inclusive start of the range to obliterate
|
|
297
|
+
* @param end - The exclusive end of the range to obliterate
|
|
298
|
+
*/
|
|
299
|
+
public obliterateRange(start: number, end: number): void {
|
|
300
|
+
this.guardReentrancy(() => this.client.obliterateRangeLocal(start, end));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @deprecated The ability to create group ops will be removed in an upcoming
|
|
305
|
+
* release, as group ops are redundant with the native batching capabilities
|
|
306
|
+
* of the runtime
|
|
307
|
+
*/
|
|
308
|
+
public groupOperation(groupOp: IMergeTreeGroupMsg) {
|
|
309
|
+
this.guardReentrancy(() => this.client.localTransaction(groupOp));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Finds the segment information (i.e. segment + offset) corresponding to a character position in the SharedString.
|
|
314
|
+
* If the position is past the end of the string, `segment` and `offset` on the returned object may be undefined.
|
|
315
|
+
* @param pos - Character position (index) into the current local view of the SharedString.
|
|
316
|
+
*/
|
|
317
|
+
public getContainingSegment(pos: number): {
|
|
318
|
+
segment: T | undefined;
|
|
319
|
+
offset: number | undefined;
|
|
320
|
+
} {
|
|
321
|
+
return this.client.getContainingSegment<T>(pos);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Returns the length of the current sequence for the client
|
|
326
|
+
*/
|
|
327
|
+
public getLength() {
|
|
328
|
+
return this.client.getLength();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Returns the current position of a segment, and -1 if the segment
|
|
333
|
+
* does not exist in this sequence
|
|
334
|
+
* @param segment - The segment to get the position of
|
|
335
|
+
*/
|
|
336
|
+
public getPosition(segment: ISegment): number {
|
|
337
|
+
return this.client.getPosition(segment);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Annotates the range with the provided properties
|
|
342
|
+
*
|
|
343
|
+
* @param start - The inclusive start position of the range to annotate
|
|
344
|
+
* @param end - The exclusive end position of the range to annotate
|
|
345
|
+
* @param props - The properties to annotate the range with
|
|
346
|
+
*
|
|
347
|
+
*/
|
|
348
|
+
public annotateRange(start: number, end: number, props: PropertySet): void {
|
|
349
|
+
this.guardReentrancy(() => this.client.annotateRangeLocal(start, end, props));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
public getPropertiesAtPosition(pos: number) {
|
|
353
|
+
return this.client.getPropertiesAtPosition(pos);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
public getRangeExtentsOfPosition(pos: number) {
|
|
357
|
+
return this.client.getRangeExtentsOfPosition(pos);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Creates a `LocalReferencePosition` on this SharedString. If the refType does not include
|
|
362
|
+
* ReferenceType.Transient, the returned reference will be added to the localRefs on the provided segment.
|
|
363
|
+
* @param segment - Segment to add the local reference on
|
|
364
|
+
* @param offset - Offset on the segment at which to place the local reference
|
|
365
|
+
* @param refType - ReferenceType for the created local reference
|
|
366
|
+
* @param properties - PropertySet to place on the created local reference
|
|
367
|
+
*/
|
|
368
|
+
public createLocalReferencePosition(
|
|
369
|
+
segment: T,
|
|
370
|
+
offset: number,
|
|
371
|
+
refType: ReferenceType,
|
|
372
|
+
properties: PropertySet | undefined,
|
|
373
|
+
slidingPreference?: SlidingPreference,
|
|
374
|
+
canSlideToEndpoint?: boolean,
|
|
375
|
+
): LocalReferencePosition {
|
|
376
|
+
return this.client.createLocalReferencePosition(
|
|
377
|
+
segment,
|
|
378
|
+
offset,
|
|
379
|
+
refType,
|
|
380
|
+
properties,
|
|
381
|
+
slidingPreference,
|
|
382
|
+
canSlideToEndpoint,
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Resolves a `ReferencePosition` into a character position using this client's perspective.
|
|
388
|
+
*/
|
|
389
|
+
public localReferencePositionToPosition(lref: ReferencePosition): number {
|
|
390
|
+
return this.client.localReferencePositionToPosition(lref);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Removes a `LocalReferencePosition` from this SharedString.
|
|
395
|
+
*/
|
|
396
|
+
public removeLocalReferencePosition(lref: LocalReferencePosition) {
|
|
397
|
+
return this.client.removeLocalReferencePosition(lref);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Resolves a remote client's position against the local sequence
|
|
402
|
+
* and returns the remote client's position relative to the local
|
|
403
|
+
* sequence. The client ref seq must be above the minimum sequence number
|
|
404
|
+
* or the return value will be undefined.
|
|
405
|
+
* Generally this method is used in conjunction with signals which provide
|
|
406
|
+
* point in time values for the below parameters, and is useful for things
|
|
407
|
+
* like displaying user position. It should not be used with persisted values
|
|
408
|
+
* as persisted values will quickly become invalid as the remoteClientRefSeq
|
|
409
|
+
* moves below the minimum sequence number
|
|
410
|
+
* @param remoteClientPosition - The remote client's position to resolve
|
|
411
|
+
* @param remoteClientRefSeq - The reference sequence number of the remote client
|
|
412
|
+
* @param remoteClientId - The client id of the remote client
|
|
413
|
+
*/
|
|
414
|
+
public resolveRemoteClientPosition(
|
|
415
|
+
remoteClientPosition: number,
|
|
416
|
+
remoteClientRefSeq: number,
|
|
417
|
+
remoteClientId: string,
|
|
418
|
+
): number | undefined {
|
|
419
|
+
return this.client.resolveRemoteClientPosition(
|
|
420
|
+
remoteClientPosition,
|
|
421
|
+
remoteClientRefSeq,
|
|
422
|
+
remoteClientId,
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private submitSequenceMessage(message: IMergeTreeOp) {
|
|
427
|
+
if (!this.isAttached()) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
const translated = makeHandlesSerializable(message, this.serializer, this.handle);
|
|
431
|
+
const metadata = this.client.peekPendingSegmentGroups(
|
|
432
|
+
message.type === MergeTreeDeltaType.GROUP ? message.ops.length : 1,
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
// if loading isn't complete, we need to cache
|
|
436
|
+
// local ops until loading is complete, and then
|
|
437
|
+
// they will be present
|
|
438
|
+
if (!this.loadedDeferred.isCompleted) {
|
|
439
|
+
this.loadedDeferredOutgoingOps.push(metadata ? [translated, metadata] : translated);
|
|
440
|
+
} else {
|
|
441
|
+
this.submitLocalMessage(translated, metadata);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Given a position specified relative to a marker id, lookup the marker
|
|
447
|
+
* and convert the position to a character position.
|
|
448
|
+
* @param relativePos - Id of marker (may be indirect) and whether position is before or after marker.
|
|
449
|
+
*/
|
|
450
|
+
public posFromRelativePos(relativePos: IRelativePosition) {
|
|
451
|
+
return this.client.posFromRelativePos(relativePos);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Walk the underlying segments of the sequence.
|
|
456
|
+
* The walked segments may extend beyond the range if the segments cross the
|
|
457
|
+
* ranges start or end boundaries.
|
|
458
|
+
*
|
|
459
|
+
* Set split range to true to ensure only segments within the range are walked.
|
|
460
|
+
*
|
|
461
|
+
* @param handler - The function to handle each segment. Traversal ends if
|
|
462
|
+
* this function returns true.
|
|
463
|
+
* @param start - Optional. The start of range walk.
|
|
464
|
+
* @param end - Optional. The end of range walk
|
|
465
|
+
* @param accum - Optional. An object that will be passed to the handler for accumulation
|
|
466
|
+
* @param splitRange - Optional. Splits boundary segments on the range boundaries
|
|
467
|
+
*/
|
|
468
|
+
public walkSegments<TClientData>(
|
|
469
|
+
handler: ISegmentAction<TClientData>,
|
|
470
|
+
start?: number,
|
|
471
|
+
end?: number,
|
|
472
|
+
accum?: TClientData,
|
|
473
|
+
splitRange: boolean = false,
|
|
474
|
+
): void {
|
|
475
|
+
this.client.walkSegments(handler, start, end, accum as TClientData, splitRange);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* @returns The most recent sequence number which has been acked by the server and processed by this
|
|
480
|
+
* SharedSegmentSequence.
|
|
481
|
+
*/
|
|
482
|
+
public getCurrentSeq() {
|
|
483
|
+
return this.client.getCurrentSeq();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Inserts a segment directly before a `ReferencePosition`.
|
|
488
|
+
* @param refPos - The reference position to insert the segment at
|
|
489
|
+
* @param segment - The segment to insert
|
|
490
|
+
*/
|
|
491
|
+
public insertAtReferencePosition(pos: ReferencePosition, segment: T): void {
|
|
492
|
+
this.guardReentrancy(() => this.client.insertAtReferencePositionLocal(pos, segment));
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Inserts a segment
|
|
496
|
+
* @param start - The position to insert the segment at
|
|
497
|
+
* @param spec - The segment to inserts spec
|
|
498
|
+
*/
|
|
499
|
+
public insertFromSpec(pos: number, spec: IJSONSegment): void {
|
|
500
|
+
const segment = this.segmentFromSpec(spec);
|
|
501
|
+
this.guardReentrancy(() => this.client.insertSegmentLocal(pos, segment));
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Retrieves the interval collection keyed on `label`. If no such interval collection exists,
|
|
506
|
+
* creates one.
|
|
507
|
+
*/
|
|
508
|
+
public getIntervalCollection(label: string): IIntervalCollection<SequenceInterval> {
|
|
509
|
+
return this.intervalCollections.get(label);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* @returns An iterable object that enumerates the IntervalCollection labels.
|
|
514
|
+
*
|
|
515
|
+
* @example
|
|
516
|
+
*
|
|
517
|
+
* ```typescript
|
|
518
|
+
* const iter = this.getIntervalCollectionKeys();
|
|
519
|
+
* for (key of iter)
|
|
520
|
+
* const collection = this.getIntervalCollection(key);
|
|
521
|
+
* ...
|
|
522
|
+
* ```
|
|
523
|
+
*/
|
|
524
|
+
public getIntervalCollectionLabels(): IterableIterator<string> {
|
|
525
|
+
return this.intervalCollections.keys();
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.summarizeCore}
|
|
530
|
+
*/
|
|
531
|
+
protected summarizeCore(
|
|
532
|
+
serializer: IFluidSerializer,
|
|
533
|
+
telemetryContext?: ITelemetryContext,
|
|
534
|
+
): ISummaryTreeWithStats {
|
|
535
|
+
const builder = new SummaryTreeBuilder();
|
|
536
|
+
|
|
537
|
+
// conditionally write the interval collection blob
|
|
538
|
+
// only if it has entries
|
|
539
|
+
if (this.intervalCollections.size > 0) {
|
|
540
|
+
builder.addBlob(snapshotFileName, this.intervalCollections.serialize(serializer));
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
builder.addWithStats(contentPath, this.summarizeMergeTree(serializer));
|
|
544
|
+
|
|
545
|
+
return builder.getSummaryTree();
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Runs serializer over the GC data for this SharedMatrix.
|
|
550
|
+
* All the IFluidHandle's represent routes to other objects.
|
|
551
|
+
*/
|
|
552
|
+
protected processGCDataCore(serializer: IFluidSerializer) {
|
|
553
|
+
if (this.intervalCollections.size > 0) {
|
|
554
|
+
this.intervalCollections.serialize(serializer);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
this.client.serializeGCData(this.handle, serializer);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Replace the range specified from start to end with the provided segment
|
|
562
|
+
* This is done by inserting the segment at the end of the range, followed
|
|
563
|
+
* by removing the contents of the range
|
|
564
|
+
* For a zero or reverse range (start \>= end), insert at end do not remove anything
|
|
565
|
+
* @param start - The start of the range to replace
|
|
566
|
+
* @param end - The end of the range to replace
|
|
567
|
+
* @param segment - The segment that will replace the range
|
|
568
|
+
*/
|
|
569
|
+
protected replaceRange(start: number, end: number, segment: ISegment): void {
|
|
570
|
+
// Insert at the max end of the range when start > end, but still remove the range later
|
|
571
|
+
const insertIndex: number = Math.max(start, end);
|
|
572
|
+
|
|
573
|
+
// Insert first, so local references can slide to the inserted seg if any
|
|
574
|
+
const insert = this.client.insertSegmentLocal(insertIndex, segment);
|
|
575
|
+
if (insert) {
|
|
576
|
+
if (start < end) {
|
|
577
|
+
this.client.removeRangeLocal(start, end);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.onConnect}
|
|
584
|
+
*/
|
|
585
|
+
protected onConnect() {
|
|
586
|
+
// Update merge tree collaboration information with new client ID and then resend pending ops
|
|
587
|
+
this.client.startOrUpdateCollaboration(this.runtime.clientId);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.onDisconnect}
|
|
592
|
+
*/
|
|
593
|
+
protected onDisconnect() {}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.reSubmitCore}
|
|
597
|
+
*/
|
|
598
|
+
protected reSubmitCore(content: any, localOpMetadata: unknown) {
|
|
599
|
+
if (
|
|
600
|
+
!this.intervalCollections.tryResubmitMessage(
|
|
601
|
+
content,
|
|
602
|
+
localOpMetadata as IMapMessageLocalMetadata,
|
|
603
|
+
)
|
|
604
|
+
) {
|
|
605
|
+
this.submitSequenceMessage(
|
|
606
|
+
this.client.regeneratePendingOp(
|
|
607
|
+
content as IMergeTreeOp,
|
|
608
|
+
localOpMetadata as SegmentGroup | SegmentGroup[],
|
|
609
|
+
),
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.loadCore}
|
|
616
|
+
*/
|
|
617
|
+
protected async loadCore(storage: IChannelStorageService) {
|
|
618
|
+
if (await storage.contains(snapshotFileName)) {
|
|
619
|
+
const blob = await storage.readBlob(snapshotFileName);
|
|
620
|
+
const header = bufferToString(blob, "utf8");
|
|
621
|
+
this.intervalCollections.populate(header);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
try {
|
|
625
|
+
// this will load the header, and return a promise
|
|
626
|
+
// that will resolve when the body is loaded
|
|
627
|
+
// and the catchup ops are available.
|
|
628
|
+
const { catchupOpsP } = await this.client.load(
|
|
629
|
+
this.runtime,
|
|
630
|
+
new ObjectStoragePartition(storage, contentPath),
|
|
631
|
+
this.serializer,
|
|
632
|
+
);
|
|
633
|
+
|
|
634
|
+
// setup a promise to process the
|
|
635
|
+
// catch up ops, and finishing the loading process
|
|
636
|
+
const loadCatchUpOps = catchupOpsP
|
|
637
|
+
.then((msgs) => {
|
|
638
|
+
msgs.forEach((m) => {
|
|
639
|
+
const collabWindow = this.client.getCollabWindow();
|
|
640
|
+
if (
|
|
641
|
+
m.minimumSequenceNumber < collabWindow.minSeq ||
|
|
642
|
+
m.referenceSequenceNumber < collabWindow.minSeq ||
|
|
643
|
+
m.sequenceNumber <= collabWindow.minSeq ||
|
|
644
|
+
// sequenceNumber could be the same if messages are part of a grouped batch
|
|
645
|
+
m.sequenceNumber < collabWindow.currentSeq
|
|
646
|
+
) {
|
|
647
|
+
throw new Error(
|
|
648
|
+
`Invalid catchup operations in snapshot: ${JSON.stringify({
|
|
649
|
+
op: {
|
|
650
|
+
seq: m.sequenceNumber,
|
|
651
|
+
minSeq: m.minimumSequenceNumber,
|
|
652
|
+
refSeq: m.referenceSequenceNumber,
|
|
653
|
+
},
|
|
654
|
+
collabWindow: {
|
|
655
|
+
seq: collabWindow.currentSeq,
|
|
656
|
+
minSeq: collabWindow.minSeq,
|
|
657
|
+
},
|
|
658
|
+
})}`,
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
this.processMergeTreeMsg(m);
|
|
662
|
+
});
|
|
663
|
+
this.loadFinished();
|
|
664
|
+
})
|
|
665
|
+
.catch((error) => {
|
|
666
|
+
this.loadFinished(error);
|
|
667
|
+
});
|
|
668
|
+
if (this.dataStoreRuntime.options?.sequenceInitializeFromHeaderOnly !== true) {
|
|
669
|
+
// if we not doing partial load, await the catch up ops,
|
|
670
|
+
// and the finalization of the load
|
|
671
|
+
await loadCatchUpOps;
|
|
672
|
+
}
|
|
673
|
+
} catch (error) {
|
|
674
|
+
this.loadFinished(error);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.processCore}
|
|
680
|
+
*/
|
|
681
|
+
protected processCore(
|
|
682
|
+
message: ISequencedDocumentMessage,
|
|
683
|
+
local: boolean,
|
|
684
|
+
localOpMetadata: unknown,
|
|
685
|
+
) {
|
|
686
|
+
// if loading isn't complete, we need to cache all
|
|
687
|
+
// incoming ops to be applied after loading is complete
|
|
688
|
+
if (this.deferIncomingOps) {
|
|
689
|
+
assert(!local, 0x072 /* "Unexpected local op when loading not finished" */);
|
|
690
|
+
this.loadedDeferredIncomingOps.push(message);
|
|
691
|
+
} else {
|
|
692
|
+
assert(
|
|
693
|
+
message.type === MessageType.Operation,
|
|
694
|
+
0x073 /* "Sequence message not operation" */,
|
|
695
|
+
);
|
|
696
|
+
|
|
697
|
+
const handled = this.intervalCollections.tryProcessMessage(
|
|
698
|
+
message.contents as IMapOperation,
|
|
699
|
+
local,
|
|
700
|
+
message,
|
|
701
|
+
localOpMetadata,
|
|
702
|
+
);
|
|
703
|
+
|
|
704
|
+
if (!handled) {
|
|
705
|
+
this.processMergeTreeMsg(message, local);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.didAttach}
|
|
712
|
+
*/
|
|
713
|
+
protected didAttach() {
|
|
714
|
+
// If we are not local, and we've attached we need to start generating and sending ops
|
|
715
|
+
// so start collaboration and provide a default client id incase we are not connected
|
|
716
|
+
if (this.isAttached()) {
|
|
717
|
+
this.client.startOrUpdateCollaboration(this.runtime.clientId ?? "attached");
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.initializeLocalCore}
|
|
723
|
+
*/
|
|
724
|
+
protected initializeLocalCore() {
|
|
725
|
+
super.initializeLocalCore();
|
|
726
|
+
this.loadFinished();
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}
|
|
731
|
+
*/
|
|
732
|
+
protected applyStashedOp(content: any): unknown {
|
|
733
|
+
return this.client.applyStashedOp(parseHandles(content, this.serializer));
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
private summarizeMergeTree(serializer: IFluidSerializer): ISummaryTreeWithStats {
|
|
737
|
+
// Are we fully loaded? If not, things will go south
|
|
738
|
+
assert(
|
|
739
|
+
this.loadedDeferred.isCompleted,
|
|
740
|
+
0x074 /* "Snapshot called when not fully loaded" */,
|
|
741
|
+
);
|
|
742
|
+
const minSeq = this.runtime.deltaManager.minimumSequenceNumber;
|
|
743
|
+
|
|
744
|
+
this.processMinSequenceNumberChanged(minSeq);
|
|
745
|
+
|
|
746
|
+
this.messagesSinceMSNChange.forEach((m) => {
|
|
747
|
+
m.minimumSequenceNumber = minSeq;
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
return this.client.summarize(
|
|
751
|
+
this.runtime,
|
|
752
|
+
this.handle,
|
|
753
|
+
serializer,
|
|
754
|
+
this.messagesSinceMSNChange,
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
private processMergeTreeMsg(rawMessage: ISequencedDocumentMessage, local?: boolean) {
|
|
759
|
+
const message = parseHandles(rawMessage, this.serializer);
|
|
760
|
+
|
|
761
|
+
const ops: IMergeTreeDeltaOp[] = [];
|
|
762
|
+
function transformOps(event: SequenceDeltaEvent) {
|
|
763
|
+
ops.push(...SharedSegmentSequence.createOpsFromDelta(event));
|
|
764
|
+
}
|
|
765
|
+
const needsTransformation = message.referenceSequenceNumber !== message.sequenceNumber - 1;
|
|
766
|
+
let stashMessage: Readonly<ISequencedDocumentMessage> = message;
|
|
767
|
+
if (this.runtime.options?.newMergeTreeSnapshotFormat !== true) {
|
|
768
|
+
if (needsTransformation) {
|
|
769
|
+
this.on("sequenceDelta", transformOps);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
this.client.applyMsg(message, local);
|
|
774
|
+
|
|
775
|
+
if (this.runtime.options?.newMergeTreeSnapshotFormat !== true) {
|
|
776
|
+
if (needsTransformation) {
|
|
777
|
+
this.removeListener("sequenceDelta", transformOps);
|
|
778
|
+
// shallow clone the message as we only overwrite top level properties,
|
|
779
|
+
// like referenceSequenceNumber and content only
|
|
780
|
+
stashMessage = {
|
|
781
|
+
...message,
|
|
782
|
+
referenceSequenceNumber: stashMessage.sequenceNumber - 1,
|
|
783
|
+
// eslint-disable-next-line import/no-deprecated
|
|
784
|
+
contents: ops.length !== 1 ? createGroupOp(...ops) : ops[0],
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
this.messagesSinceMSNChange.push(stashMessage);
|
|
789
|
+
|
|
790
|
+
// Do GC every once in a while...
|
|
791
|
+
if (
|
|
792
|
+
this.messagesSinceMSNChange.length > 20 &&
|
|
793
|
+
this.messagesSinceMSNChange[20].sequenceNumber < message.minimumSequenceNumber
|
|
794
|
+
) {
|
|
795
|
+
this.processMinSequenceNumberChanged(message.minimumSequenceNumber);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
private processMinSequenceNumberChanged(minSeq: number) {
|
|
801
|
+
let index = 0;
|
|
802
|
+
for (; index < this.messagesSinceMSNChange.length; index++) {
|
|
803
|
+
if (this.messagesSinceMSNChange[index].sequenceNumber > minSeq) {
|
|
804
|
+
break;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
if (index !== 0) {
|
|
808
|
+
this.messagesSinceMSNChange = this.messagesSinceMSNChange.slice(index);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
private loadFinished(error?: any) {
|
|
813
|
+
if (!this.loadedDeferred.isCompleted) {
|
|
814
|
+
// Initialize the interval collections
|
|
815
|
+
this.initializeIntervalCollections();
|
|
816
|
+
if (error) {
|
|
817
|
+
this.loadedDeferred.reject(error);
|
|
818
|
+
throw error;
|
|
819
|
+
} else {
|
|
820
|
+
// it is important this series remains synchronous
|
|
821
|
+
// first we stop deferring incoming ops, and apply then all
|
|
822
|
+
this.deferIncomingOps = false;
|
|
823
|
+
for (const message of this.loadedDeferredIncomingOps) {
|
|
824
|
+
this.processCore(message, false, undefined);
|
|
825
|
+
}
|
|
826
|
+
this.loadedDeferredIncomingOps.length = 0;
|
|
827
|
+
|
|
828
|
+
// then resolve the loaded promise
|
|
829
|
+
// and resubmit all the outstanding ops, as the snapshot
|
|
830
|
+
// is fully loaded, and all outstanding ops are applied
|
|
831
|
+
this.loadedDeferred.resolve();
|
|
832
|
+
|
|
833
|
+
for (const [messageContent, opMetadata] of this.loadedDeferredOutgoingOps) {
|
|
834
|
+
this.reSubmitCore(messageContent, opMetadata);
|
|
835
|
+
}
|
|
836
|
+
this.loadedDeferredOutgoingOps.length = 0;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
private initializeIntervalCollections() {
|
|
842
|
+
// Listen and initialize new SharedIntervalCollections
|
|
843
|
+
this.intervalCollections.eventEmitter.on(
|
|
844
|
+
"create",
|
|
845
|
+
({ key, previousValue }: IValueChanged, local: boolean) => {
|
|
846
|
+
const intervalCollection = this.intervalCollections.get(key);
|
|
847
|
+
if (!intervalCollection.attached) {
|
|
848
|
+
intervalCollection.attachGraph(this.client, key);
|
|
849
|
+
}
|
|
850
|
+
assert(
|
|
851
|
+
previousValue === undefined,
|
|
852
|
+
0x2c1 /* "Creating an interval collection that already exists?" */,
|
|
853
|
+
);
|
|
854
|
+
this.emit("createIntervalCollection", key, local, this);
|
|
855
|
+
},
|
|
856
|
+
);
|
|
857
|
+
|
|
858
|
+
// Initialize existing SharedIntervalCollections
|
|
859
|
+
for (const key of this.intervalCollections.keys()) {
|
|
860
|
+
const intervalCollection = this.intervalCollections.get(key);
|
|
861
|
+
intervalCollection.attachGraph(this.client, key);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
function createReentrancyDetector(
|
|
867
|
+
onReentrancy: (depth: number) => void,
|
|
868
|
+
): <T>(callback: () => T) => T {
|
|
869
|
+
let depth = 0;
|
|
870
|
+
function detectReentrancy<T>(callback: () => T): T {
|
|
871
|
+
if (depth > 0) {
|
|
872
|
+
onReentrancy(depth);
|
|
873
|
+
}
|
|
874
|
+
depth++;
|
|
875
|
+
try {
|
|
876
|
+
return callback();
|
|
877
|
+
} finally {
|
|
878
|
+
depth--;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
return detectReentrancy;
|
|
747
883
|
}
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Apps which generate reentrant behavior may do so at a high frequency.
|
|
887
|
+
* Logging even per-SharedSegmentSequence instance might be too noisy, and having a few logs from a session
|
|
888
|
+
* is likely enough.
|
|
889
|
+
*/
|
|
890
|
+
let totalReentrancyLogs = 3;
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Resets the reentrancy log counter. Test-only API.
|
|
894
|
+
*/
|
|
895
|
+
export function resetReentrancyLogCounter() {
|
|
896
|
+
totalReentrancyLogs = 3;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const reentrancyErrorMessage = "Reentrancy detected in sequence local ops";
|
|
900
|
+
const ensureNoReentrancy = createReentrancyDetector(() => {
|
|
901
|
+
throw new LoggingError(reentrancyErrorMessage);
|
|
902
|
+
});
|