@fluidframework/sequence 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.225277
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-esm.json +4 -0
- package/api-extractor-lint.json +4 -0
- package/api-extractor.json +2 -2
- package/api-report/sequence.api.md +734 -0
- package/dist/{defaultMap.js → defaultMap.cjs} +38 -26
- 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 +45 -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 +1193 -0
- package/dist/intervalCollection.cjs.map +1 -0
- package/dist/intervalCollection.d.ts +448 -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 +24 -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 +90 -0
- package/dist/intervals/intervalUtils.cjs.map +1 -0
- package/dist/intervals/intervalUtils.d.ts +240 -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 +1290 -0
- package/dist/sequence-beta.d.ts +244 -0
- package/dist/sequence-public.d.ts +244 -0
- package/dist/sequence-untrimmed.d.ts +1778 -0
- package/dist/{sequence.js → sequence.cjs} +230 -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 +274 -0
- package/dist/sharedString.cjs.map +1 -0
- package/dist/sharedString.d.ts +49 -26
- package/dist/sharedString.d.ts.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/lib/{defaultMap.d.ts → defaultMap.d.mts} +8 -7
- package/lib/defaultMap.d.mts.map +1 -0
- package/lib/{defaultMap.js → defaultMap.mjs} +38 -26
- package/lib/defaultMap.mjs.map +1 -0
- package/lib/{defaultMapInterfaces.d.ts → defaultMapInterfaces.d.mts} +46 -13
- 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 +556 -0
- package/lib/intervalCollection.d.mts.map +1 -0
- package/lib/intervalCollection.mjs +1178 -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 +240 -0
- package/lib/intervals/intervalUtils.d.mts.map +1 -0
- package/lib/intervals/intervalUtils.mjs +84 -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} +4 -3
- 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} +2 -2
- package/lib/packageVersion.d.mts.map +1 -0
- 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 +1277 -0
- package/lib/sequence-beta.d.mts +231 -0
- package/lib/sequence-public.d.mts +231 -0
- package/lib/sequence-untrimmed.d.mts +1765 -0
- package/lib/{sequence.d.ts → sequence.d.mts} +128 -51
- package/lib/sequence.d.mts.map +1 -0
- package/lib/{sequence.js → sequence.mjs} +229 -152
- package/lib/sequence.mjs.map +1 -0
- package/lib/{sequenceDeltaEvent.d.ts → sequenceDeltaEvent.d.mts} +25 -8
- 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} +13 -13
- 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} +16 -4
- 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} +52 -29
- package/lib/sharedString.d.mts.map +1 -0
- package/lib/sharedString.mjs +269 -0
- package/lib/sharedString.mjs.map +1 -0
- package/package.json +168 -75
- package/prettier.config.cjs +8 -0
- package/src/defaultMap.ts +419 -403
- package/src/defaultMapInterfaces.ts +164 -117
- package/src/index.ts +86 -26
- package/src/intervalCollection.ts +2042 -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 +26 -0
- package/src/intervals/interval.ts +238 -0
- package/src/intervals/intervalUtils.ts +298 -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 +850 -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 +367 -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.d.ts.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
|
@@ -0,0 +1,1193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*!
|
|
3
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.intervalLocatorFromEndpoint = exports.IntervalCollection = exports.makeOpsMap = exports.IntervalCollectionValueType = exports.SequenceIntervalCollectionValueType = exports.LocalIntervalCollection = exports.createIntervalIndex = exports.computeStickinessFromSide = exports.endpointPosAndSide = exports.sidesFromStickiness = exports.Side = void 0;
|
|
8
|
+
/* eslint-disable no-bitwise */
|
|
9
|
+
/* eslint-disable import/no-deprecated */
|
|
10
|
+
const client_utils_1 = require("@fluid-internal/client-utils");
|
|
11
|
+
const core_utils_1 = require("@fluidframework/core-utils");
|
|
12
|
+
const merge_tree_1 = require("@fluidframework/merge-tree");
|
|
13
|
+
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
14
|
+
const uuid_1 = require("uuid");
|
|
15
|
+
const intervals_1 = require("./intervals/index.cjs");
|
|
16
|
+
const intervalIndex_1 = require("./intervalIndex/index.cjs");
|
|
17
|
+
/**
|
|
18
|
+
* Defines a side relative to a character in a sequence.
|
|
19
|
+
*
|
|
20
|
+
* @remarks See {@link SequencePlace} for additional context on usage.
|
|
21
|
+
* @alpha
|
|
22
|
+
*/
|
|
23
|
+
var Side;
|
|
24
|
+
(function (Side) {
|
|
25
|
+
Side[Side["Before"] = 0] = "Before";
|
|
26
|
+
Side[Side["After"] = 1] = "After";
|
|
27
|
+
})(Side || (exports.Side = Side = {}));
|
|
28
|
+
const reservedIntervalIdKey = "intervalId";
|
|
29
|
+
function sidesFromStickiness(stickiness) {
|
|
30
|
+
const startSide = (stickiness & intervals_1.IntervalStickiness.START) !== 0 ? Side.After : Side.Before;
|
|
31
|
+
const endSide = (stickiness & intervals_1.IntervalStickiness.END) !== 0 ? Side.Before : Side.After;
|
|
32
|
+
return { startSide, endSide };
|
|
33
|
+
}
|
|
34
|
+
exports.sidesFromStickiness = sidesFromStickiness;
|
|
35
|
+
/**
|
|
36
|
+
* Decompress an interval after loading a summary from JSON. The exact format
|
|
37
|
+
* of this compression is unspecified and subject to change
|
|
38
|
+
*/
|
|
39
|
+
function decompressInterval(interval, label) {
|
|
40
|
+
const stickiness = interval[5] ?? intervals_1.IntervalStickiness.END;
|
|
41
|
+
const { startSide, endSide } = sidesFromStickiness(stickiness);
|
|
42
|
+
return {
|
|
43
|
+
start: interval[0],
|
|
44
|
+
end: interval[1],
|
|
45
|
+
sequenceNumber: interval[2],
|
|
46
|
+
intervalType: interval[3],
|
|
47
|
+
properties: { ...interval[4], [merge_tree_1.reservedRangeLabelsKey]: [label] },
|
|
48
|
+
stickiness,
|
|
49
|
+
startSide,
|
|
50
|
+
endSide,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Compress an interval prior to serialization as JSON. The exact format of this
|
|
55
|
+
* compression is unspecified and subject to change
|
|
56
|
+
*/
|
|
57
|
+
function compressInterval(interval) {
|
|
58
|
+
const { start, end, sequenceNumber, intervalType, properties } = interval;
|
|
59
|
+
let base = [
|
|
60
|
+
start,
|
|
61
|
+
end,
|
|
62
|
+
sequenceNumber,
|
|
63
|
+
intervalType,
|
|
64
|
+
// remove the `referenceRangeLabels` property as it is already stored
|
|
65
|
+
// in the `label` field of the summary
|
|
66
|
+
{ ...properties, [merge_tree_1.reservedRangeLabelsKey]: undefined },
|
|
67
|
+
];
|
|
68
|
+
if (interval.stickiness !== undefined && interval.stickiness !== intervals_1.IntervalStickiness.END) {
|
|
69
|
+
// reassignment to make it easier for typescript to reason about types
|
|
70
|
+
base = [...base, interval.stickiness];
|
|
71
|
+
}
|
|
72
|
+
return base;
|
|
73
|
+
}
|
|
74
|
+
function endpointPosAndSide(start, end) {
|
|
75
|
+
const startIsPlainEndpoint = typeof start === "number" || start === "start" || start === "end";
|
|
76
|
+
const endIsPlainEndpoint = typeof end === "number" || end === "start" || end === "end";
|
|
77
|
+
const startSide = startIsPlainEndpoint ? Side.Before : start?.side;
|
|
78
|
+
const endSide = endIsPlainEndpoint ? Side.Before : end?.side;
|
|
79
|
+
const startPos = startIsPlainEndpoint ? start : start?.pos;
|
|
80
|
+
const endPos = endIsPlainEndpoint ? end : end?.pos;
|
|
81
|
+
return {
|
|
82
|
+
startSide,
|
|
83
|
+
endSide,
|
|
84
|
+
startPos,
|
|
85
|
+
endPos,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
exports.endpointPosAndSide = endpointPosAndSide;
|
|
89
|
+
function toSequencePlace(pos, side) {
|
|
90
|
+
return typeof pos === "number" ? { pos, side } : pos;
|
|
91
|
+
}
|
|
92
|
+
function toOptionalSequencePlace(pos, side = Side.Before) {
|
|
93
|
+
return typeof pos === "number" ? { pos, side } : pos;
|
|
94
|
+
}
|
|
95
|
+
function computeStickinessFromSide(startPos = -1, startSide = Side.Before, endPos = -1, endSide = Side.Before) {
|
|
96
|
+
let stickiness = intervals_1.IntervalStickiness.NONE;
|
|
97
|
+
if (startSide === Side.After || startPos === "start") {
|
|
98
|
+
stickiness |= intervals_1.IntervalStickiness.START;
|
|
99
|
+
}
|
|
100
|
+
if (endSide === Side.Before || endPos === "end") {
|
|
101
|
+
stickiness |= intervals_1.IntervalStickiness.END;
|
|
102
|
+
}
|
|
103
|
+
return stickiness;
|
|
104
|
+
}
|
|
105
|
+
exports.computeStickinessFromSide = computeStickinessFromSide;
|
|
106
|
+
function createIntervalIndex() {
|
|
107
|
+
const helpers = {
|
|
108
|
+
create: intervals_1.createInterval,
|
|
109
|
+
};
|
|
110
|
+
const lc = new LocalIntervalCollection(undefined, "", helpers, {});
|
|
111
|
+
return lc;
|
|
112
|
+
}
|
|
113
|
+
exports.createIntervalIndex = createIntervalIndex;
|
|
114
|
+
class LocalIntervalCollection {
|
|
115
|
+
constructor(client, label, helpers, options,
|
|
116
|
+
/** Callback invoked each time one of the endpoints of an interval slides. */
|
|
117
|
+
onPositionChange) {
|
|
118
|
+
this.client = client;
|
|
119
|
+
this.label = label;
|
|
120
|
+
this.helpers = helpers;
|
|
121
|
+
this.options = options;
|
|
122
|
+
this.onPositionChange = onPositionChange;
|
|
123
|
+
this.overlappingIntervalsIndex = new intervalIndex_1.OverlappingIntervalsIndex(client, helpers);
|
|
124
|
+
this.idIntervalIndex = (0, intervalIndex_1.createIdIntervalIndex)();
|
|
125
|
+
this.endIntervalIndex = new intervalIndex_1.EndpointIndex(client, helpers);
|
|
126
|
+
this.indexes = new Set([
|
|
127
|
+
this.overlappingIntervalsIndex,
|
|
128
|
+
this.idIntervalIndex,
|
|
129
|
+
this.endIntervalIndex,
|
|
130
|
+
]);
|
|
131
|
+
}
|
|
132
|
+
createLegacyId(start, end) {
|
|
133
|
+
// Create a non-unique ID based on start and end to be used on intervals that come from legacy clients
|
|
134
|
+
// without ID's.
|
|
135
|
+
return `${LocalIntervalCollection.legacyIdPrefix}${start}-${end}`;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Validates that a serialized interval has the ID property. Creates an ID
|
|
139
|
+
* if one does not already exist
|
|
140
|
+
*
|
|
141
|
+
* @param serializedInterval - The interval to be checked
|
|
142
|
+
* @returns The interval's existing or newly created id
|
|
143
|
+
*/
|
|
144
|
+
ensureSerializedId(serializedInterval) {
|
|
145
|
+
let id = serializedInterval.properties?.[reservedIntervalIdKey];
|
|
146
|
+
if (id === undefined) {
|
|
147
|
+
// Back-compat: 0.39 and earlier did not have IDs on intervals. If an interval from such a client
|
|
148
|
+
// comes over the wire, create a non-unique one based on start/end.
|
|
149
|
+
// This will allow all clients to refer to this interval consistently.
|
|
150
|
+
id = this.createLegacyId(serializedInterval.start, serializedInterval.end);
|
|
151
|
+
const newProps = {
|
|
152
|
+
[reservedIntervalIdKey]: id,
|
|
153
|
+
};
|
|
154
|
+
serializedInterval.properties = (0, merge_tree_1.addProperties)(serializedInterval.properties, newProps);
|
|
155
|
+
}
|
|
156
|
+
// Make the ID immutable for safety's sake.
|
|
157
|
+
Object.defineProperty(serializedInterval.properties, reservedIntervalIdKey, {
|
|
158
|
+
configurable: false,
|
|
159
|
+
enumerable: true,
|
|
160
|
+
writable: false,
|
|
161
|
+
});
|
|
162
|
+
return id;
|
|
163
|
+
}
|
|
164
|
+
removeIntervalFromIndexes(interval) {
|
|
165
|
+
for (const index of this.indexes) {
|
|
166
|
+
index.remove(interval);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
appendIndex(index) {
|
|
170
|
+
this.indexes.add(index);
|
|
171
|
+
}
|
|
172
|
+
removeIndex(index) {
|
|
173
|
+
return this.indexes.delete(index);
|
|
174
|
+
}
|
|
175
|
+
removeExistingInterval(interval) {
|
|
176
|
+
this.removeIntervalFromIndexes(interval);
|
|
177
|
+
this.removeIntervalListeners(interval);
|
|
178
|
+
}
|
|
179
|
+
createInterval(start, end, intervalType, op) {
|
|
180
|
+
return this.helpers.create(this.label, start, end, this.client, intervalType, op, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint);
|
|
181
|
+
}
|
|
182
|
+
addInterval(start, end, intervalType, props, op) {
|
|
183
|
+
var _a;
|
|
184
|
+
const interval = this.createInterval(start, end, intervalType, op);
|
|
185
|
+
if (interval) {
|
|
186
|
+
if (!interval.properties) {
|
|
187
|
+
interval.properties = (0, merge_tree_1.createMap)();
|
|
188
|
+
}
|
|
189
|
+
if (props) {
|
|
190
|
+
// This check is intended to prevent scenarios where a random interval is created and then
|
|
191
|
+
// inserted into a collection. The aim is to ensure that the collection is created first
|
|
192
|
+
// then the user can create/add intervals based on the collection
|
|
193
|
+
if (props[merge_tree_1.reservedRangeLabelsKey] !== undefined &&
|
|
194
|
+
props[merge_tree_1.reservedRangeLabelsKey][0] !== this.label) {
|
|
195
|
+
throw new telemetry_utils_1.LoggingError("Adding an interval that belongs to another interval collection is not permitted");
|
|
196
|
+
}
|
|
197
|
+
interval.addProperties(props);
|
|
198
|
+
}
|
|
199
|
+
(_a = interval.properties)[reservedIntervalIdKey] ?? (_a[reservedIntervalIdKey] = (0, uuid_1.v4)());
|
|
200
|
+
this.add(interval);
|
|
201
|
+
}
|
|
202
|
+
return interval;
|
|
203
|
+
}
|
|
204
|
+
linkEndpointsToInterval(interval) {
|
|
205
|
+
if (interval instanceof intervals_1.SequenceInterval) {
|
|
206
|
+
interval.start.addProperties({ interval });
|
|
207
|
+
interval.end.addProperties({ interval });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
addIntervalToIndexes(interval) {
|
|
211
|
+
for (const index of this.indexes) {
|
|
212
|
+
index.add(interval);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
add(interval) {
|
|
216
|
+
this.linkEndpointsToInterval(interval);
|
|
217
|
+
this.addIntervalToIndexes(interval);
|
|
218
|
+
this.addIntervalListeners(interval);
|
|
219
|
+
}
|
|
220
|
+
changeInterval(interval, start, end, op, localSeq) {
|
|
221
|
+
const newInterval = interval.modify(this.label, start, end, op, localSeq, this.options.mergeTreeReferencesCanSlideToEndpoint);
|
|
222
|
+
if (newInterval) {
|
|
223
|
+
this.removeExistingInterval(interval);
|
|
224
|
+
this.add(newInterval);
|
|
225
|
+
}
|
|
226
|
+
return newInterval;
|
|
227
|
+
}
|
|
228
|
+
serialize() {
|
|
229
|
+
return {
|
|
230
|
+
label: this.label,
|
|
231
|
+
intervals: Array.from(this.idIntervalIndex, (interval) => compressInterval(interval.serialize())),
|
|
232
|
+
version: 2,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
addIntervalListeners(interval) {
|
|
236
|
+
const cloneRef = (ref) => {
|
|
237
|
+
const segment = ref.getSegment();
|
|
238
|
+
if (segment === undefined) {
|
|
239
|
+
// Cloning is unnecessary: refs which have slid off the string entirely
|
|
240
|
+
// never get slid back on. Creation code for refs doesn't accept undefined segment
|
|
241
|
+
// either, so this must be special-cased.
|
|
242
|
+
return ref;
|
|
243
|
+
}
|
|
244
|
+
return this.client.createLocalReferencePosition(segment, ref.getOffset(), merge_tree_1.ReferenceType.Transient, ref.properties, ref.slidingPreference, ref.canSlideToEndpoint);
|
|
245
|
+
};
|
|
246
|
+
if (interval instanceof intervals_1.SequenceInterval) {
|
|
247
|
+
let previousInterval;
|
|
248
|
+
let pendingChanges = 0;
|
|
249
|
+
interval.addPositionChangeListeners(() => {
|
|
250
|
+
pendingChanges++;
|
|
251
|
+
// Note: both start and end can change and invoke beforeSlide on each endpoint before afterSlide.
|
|
252
|
+
if (!previousInterval) {
|
|
253
|
+
previousInterval = interval.clone();
|
|
254
|
+
previousInterval.start = cloneRef(previousInterval.start);
|
|
255
|
+
previousInterval.end = cloneRef(previousInterval.end);
|
|
256
|
+
this.removeIntervalFromIndexes(interval);
|
|
257
|
+
}
|
|
258
|
+
}, () => {
|
|
259
|
+
(0, core_utils_1.assert)(previousInterval !== undefined, 0x3fa /* Invalid interleaving of before/after slide */);
|
|
260
|
+
pendingChanges--;
|
|
261
|
+
if (pendingChanges === 0) {
|
|
262
|
+
this.addIntervalToIndexes(interval);
|
|
263
|
+
this.onPositionChange?.(interval, previousInterval);
|
|
264
|
+
previousInterval = undefined;
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
removeIntervalListeners(interval) {
|
|
270
|
+
if (interval instanceof intervals_1.SequenceInterval) {
|
|
271
|
+
interval.removePositionChangeListeners();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
exports.LocalIntervalCollection = LocalIntervalCollection;
|
|
276
|
+
LocalIntervalCollection.legacyIdPrefix = "legacy";
|
|
277
|
+
class SequenceIntervalCollectionFactory {
|
|
278
|
+
load(emitter, raw = [], options) {
|
|
279
|
+
return new IntervalCollection(intervals_1.sequenceIntervalHelpers, true, emitter, raw, options);
|
|
280
|
+
}
|
|
281
|
+
store(value) {
|
|
282
|
+
return value.serializeInternal();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
class SequenceIntervalCollectionValueType {
|
|
286
|
+
get name() {
|
|
287
|
+
return SequenceIntervalCollectionValueType.Name;
|
|
288
|
+
}
|
|
289
|
+
get factory() {
|
|
290
|
+
return SequenceIntervalCollectionValueType._factory;
|
|
291
|
+
}
|
|
292
|
+
get ops() {
|
|
293
|
+
return SequenceIntervalCollectionValueType._ops;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
exports.SequenceIntervalCollectionValueType = SequenceIntervalCollectionValueType;
|
|
297
|
+
SequenceIntervalCollectionValueType.Name = "sharedStringIntervalCollection";
|
|
298
|
+
SequenceIntervalCollectionValueType._factory = new SequenceIntervalCollectionFactory();
|
|
299
|
+
SequenceIntervalCollectionValueType._ops = makeOpsMap();
|
|
300
|
+
class IntervalCollectionFactory {
|
|
301
|
+
load(emitter, raw = [], options) {
|
|
302
|
+
const helpers = {
|
|
303
|
+
create: intervals_1.createInterval,
|
|
304
|
+
};
|
|
305
|
+
const collection = new IntervalCollection(helpers, false, emitter, raw, options);
|
|
306
|
+
collection.attachGraph(undefined, "");
|
|
307
|
+
return collection;
|
|
308
|
+
}
|
|
309
|
+
store(value) {
|
|
310
|
+
return value.serializeInternal();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
class IntervalCollectionValueType {
|
|
314
|
+
get name() {
|
|
315
|
+
return IntervalCollectionValueType.Name;
|
|
316
|
+
}
|
|
317
|
+
get factory() {
|
|
318
|
+
return IntervalCollectionValueType._factory;
|
|
319
|
+
}
|
|
320
|
+
get ops() {
|
|
321
|
+
return IntervalCollectionValueType._ops;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
exports.IntervalCollectionValueType = IntervalCollectionValueType;
|
|
325
|
+
IntervalCollectionValueType.Name = "sharedIntervalCollection";
|
|
326
|
+
IntervalCollectionValueType._factory = new IntervalCollectionFactory();
|
|
327
|
+
IntervalCollectionValueType._ops = makeOpsMap();
|
|
328
|
+
function makeOpsMap() {
|
|
329
|
+
const rebase = (collection, op, localOpMetadata) => {
|
|
330
|
+
const { localSeq } = localOpMetadata;
|
|
331
|
+
const rebasedValue = collection.rebaseLocalInterval(op.opName, op.value, localSeq);
|
|
332
|
+
if (rebasedValue === undefined) {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
const rebasedOp = { ...op, value: rebasedValue };
|
|
336
|
+
return { rebasedOp, rebasedLocalOpMetadata: localOpMetadata };
|
|
337
|
+
};
|
|
338
|
+
const applyStashedOp = (collection, op) => {
|
|
339
|
+
return collection.applyStashedOp(op);
|
|
340
|
+
};
|
|
341
|
+
return new Map([
|
|
342
|
+
[
|
|
343
|
+
intervals_1.IntervalOpType.ADD,
|
|
344
|
+
{
|
|
345
|
+
process: (collection, params, local, op, localOpMetadata) => {
|
|
346
|
+
// if params is undefined, the interval was deleted during
|
|
347
|
+
// rebasing
|
|
348
|
+
if (!params) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
(0, core_utils_1.assert)(op !== undefined, 0x3fb /* op should exist here */);
|
|
352
|
+
collection.ackAdd(params, local, op, localOpMetadata);
|
|
353
|
+
},
|
|
354
|
+
rebase,
|
|
355
|
+
applyStashedOp,
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
[
|
|
359
|
+
intervals_1.IntervalOpType.DELETE,
|
|
360
|
+
{
|
|
361
|
+
process: (collection, params, local, op) => {
|
|
362
|
+
(0, core_utils_1.assert)(op !== undefined, 0x3fc /* op should exist here */);
|
|
363
|
+
collection.ackDelete(params, local, op);
|
|
364
|
+
},
|
|
365
|
+
rebase: (collection, op, localOpMetadata) => {
|
|
366
|
+
// Deletion of intervals is based on id, so requires no rebasing.
|
|
367
|
+
return { rebasedOp: op, rebasedLocalOpMetadata: localOpMetadata };
|
|
368
|
+
},
|
|
369
|
+
applyStashedOp,
|
|
370
|
+
},
|
|
371
|
+
],
|
|
372
|
+
[
|
|
373
|
+
intervals_1.IntervalOpType.CHANGE,
|
|
374
|
+
{
|
|
375
|
+
process: (collection, params, local, op, localOpMetadata) => {
|
|
376
|
+
// if params is undefined, the interval was deleted during
|
|
377
|
+
// rebasing
|
|
378
|
+
if (!params) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
(0, core_utils_1.assert)(op !== undefined, 0x3fd /* op should exist here */);
|
|
382
|
+
collection.ackChange(params, local, op, localOpMetadata);
|
|
383
|
+
},
|
|
384
|
+
rebase,
|
|
385
|
+
applyStashedOp,
|
|
386
|
+
},
|
|
387
|
+
],
|
|
388
|
+
]);
|
|
389
|
+
}
|
|
390
|
+
exports.makeOpsMap = makeOpsMap;
|
|
391
|
+
class IntervalCollectionIterator {
|
|
392
|
+
constructor(collection, iteratesForward = true, start, end) {
|
|
393
|
+
this.results = [];
|
|
394
|
+
this.index = 0;
|
|
395
|
+
collection.gatherIterationResults(this.results, iteratesForward, start, end);
|
|
396
|
+
}
|
|
397
|
+
next() {
|
|
398
|
+
if (this.index < this.results.length) {
|
|
399
|
+
return {
|
|
400
|
+
value: this.results[this.index++],
|
|
401
|
+
done: false,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
value: undefined,
|
|
406
|
+
done: true,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
// solely for type checking in the implementation of add - will be removed once
|
|
411
|
+
// deprecated signatures are removed
|
|
412
|
+
const isSequencePlace = (place) => {
|
|
413
|
+
return typeof place === "number" || typeof place === "string" || place.pos !== undefined;
|
|
414
|
+
};
|
|
415
|
+
/**
|
|
416
|
+
* {@inheritdoc IIntervalCollection}
|
|
417
|
+
*/
|
|
418
|
+
class IntervalCollection extends client_utils_1.TypedEventEmitter {
|
|
419
|
+
get attached() {
|
|
420
|
+
return !!this.localCollection;
|
|
421
|
+
}
|
|
422
|
+
/** @internal */
|
|
423
|
+
constructor(helpers, requiresClient, emitter, serializedIntervals, options = {}) {
|
|
424
|
+
super();
|
|
425
|
+
this.helpers = helpers;
|
|
426
|
+
this.requiresClient = requiresClient;
|
|
427
|
+
this.emitter = emitter;
|
|
428
|
+
this.options = options;
|
|
429
|
+
this.localSeqToSerializedInterval = new Map();
|
|
430
|
+
this.localSeqToRebasedInterval = new Map();
|
|
431
|
+
this.pendingChangesStart = new Map();
|
|
432
|
+
this.pendingChangesEnd = new Map();
|
|
433
|
+
this.savedSerializedIntervals = Array.isArray(serializedIntervals)
|
|
434
|
+
? serializedIntervals
|
|
435
|
+
: serializedIntervals.intervals.map((i) => decompressInterval(i, serializedIntervals.label));
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* {@inheritdoc IIntervalCollection.attachIndex}
|
|
439
|
+
*/
|
|
440
|
+
attachIndex(index) {
|
|
441
|
+
if (!this.attached) {
|
|
442
|
+
throw new telemetry_utils_1.LoggingError("The local interval collection must exist");
|
|
443
|
+
}
|
|
444
|
+
for (const interval of this) {
|
|
445
|
+
index.add(interval);
|
|
446
|
+
}
|
|
447
|
+
this.localCollection?.appendIndex(index);
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* {@inheritdoc IIntervalCollection.detachIndex}
|
|
451
|
+
*/
|
|
452
|
+
detachIndex(index) {
|
|
453
|
+
if (!this.attached) {
|
|
454
|
+
throw new telemetry_utils_1.LoggingError("The local interval collection must exist");
|
|
455
|
+
}
|
|
456
|
+
// Avoid removing intervals if the index does not exist
|
|
457
|
+
if (!this.localCollection?.removeIndex(index)) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
for (const interval of this) {
|
|
461
|
+
index.remove(interval);
|
|
462
|
+
}
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
rebasePositionWithSegmentSlide(pos, seqNumberFrom, localSeq) {
|
|
466
|
+
if (!this.client) {
|
|
467
|
+
throw new telemetry_utils_1.LoggingError("mergeTree client must exist");
|
|
468
|
+
}
|
|
469
|
+
if (pos === "start" || pos === "end") {
|
|
470
|
+
return pos;
|
|
471
|
+
}
|
|
472
|
+
const { clientId } = this.client.getCollabWindow();
|
|
473
|
+
const { segment, offset } = this.client.getContainingSegment(pos, {
|
|
474
|
+
referenceSequenceNumber: seqNumberFrom,
|
|
475
|
+
clientId: this.client.getLongClientId(clientId),
|
|
476
|
+
}, localSeq);
|
|
477
|
+
// if segment is undefined, it slid off the string
|
|
478
|
+
(0, core_utils_1.assert)(segment !== undefined, 0x54e /* No segment found */);
|
|
479
|
+
const segoff = (0, merge_tree_1.getSlideToSegoff)({ segment, offset }, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint) ?? segment;
|
|
480
|
+
// case happens when rebasing op, but concurrently entire string has been deleted
|
|
481
|
+
if (segoff.segment === undefined || segoff.offset === undefined) {
|
|
482
|
+
return merge_tree_1.DetachedReferencePosition;
|
|
483
|
+
}
|
|
484
|
+
(0, core_utils_1.assert)(offset !== undefined && 0 <= offset && offset < segment.cachedLength, 0x54f /* Invalid offset */);
|
|
485
|
+
return this.client.findReconnectionPosition(segoff.segment, localSeq) + segoff.offset;
|
|
486
|
+
}
|
|
487
|
+
computeRebasedPositions(localSeq) {
|
|
488
|
+
(0, core_utils_1.assert)(this.client !== undefined, 0x550 /* Client should be defined when computing rebased position */);
|
|
489
|
+
const original = this.localSeqToSerializedInterval.get(localSeq);
|
|
490
|
+
(0, core_utils_1.assert)(original !== undefined, 0x551 /* Failed to store pending serialized interval info for this localSeq. */);
|
|
491
|
+
const rebased = { ...original };
|
|
492
|
+
const { start, end, sequenceNumber } = original;
|
|
493
|
+
if (start !== undefined) {
|
|
494
|
+
rebased.start = this.rebasePositionWithSegmentSlide(start, sequenceNumber, localSeq);
|
|
495
|
+
}
|
|
496
|
+
if (end !== undefined) {
|
|
497
|
+
rebased.end = this.rebasePositionWithSegmentSlide(end, sequenceNumber, localSeq);
|
|
498
|
+
}
|
|
499
|
+
return rebased;
|
|
500
|
+
}
|
|
501
|
+
/** @internal */
|
|
502
|
+
attachGraph(client, label) {
|
|
503
|
+
if (this.attached) {
|
|
504
|
+
throw new telemetry_utils_1.LoggingError("Only supports one Sequence attach");
|
|
505
|
+
}
|
|
506
|
+
if (client === undefined && this.requiresClient) {
|
|
507
|
+
throw new telemetry_utils_1.LoggingError("Client required for this collection");
|
|
508
|
+
}
|
|
509
|
+
// Instantiate the local interval collection based on the saved intervals
|
|
510
|
+
this.client = client;
|
|
511
|
+
if (client) {
|
|
512
|
+
client.on("normalize", () => {
|
|
513
|
+
for (const localSeq of this.localSeqToSerializedInterval.keys()) {
|
|
514
|
+
this.localSeqToRebasedInterval.set(localSeq, this.computeRebasedPositions(localSeq));
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
this.localCollection = new LocalIntervalCollection(client, label, this.helpers, this.options, (interval, previousInterval) => this.emitChange(interval, previousInterval, true, true));
|
|
519
|
+
if (this.savedSerializedIntervals) {
|
|
520
|
+
for (const serializedInterval of this.savedSerializedIntervals) {
|
|
521
|
+
this.localCollection.ensureSerializedId(serializedInterval);
|
|
522
|
+
const { start: startPos, end: endPos, intervalType, properties, startSide, endSide, } = serializedInterval;
|
|
523
|
+
const start = typeof startPos === "number" && startSide !== undefined
|
|
524
|
+
? { pos: startPos, side: startSide }
|
|
525
|
+
: startPos;
|
|
526
|
+
const end = typeof endPos === "number" && endSide !== undefined
|
|
527
|
+
? { pos: endPos, side: endSide }
|
|
528
|
+
: endPos;
|
|
529
|
+
const interval = this.helpers.create(label, start, end, client, intervalType, undefined, true, this.options.mergeTreeReferencesCanSlideToEndpoint);
|
|
530
|
+
if (properties) {
|
|
531
|
+
interval.addProperties(properties);
|
|
532
|
+
}
|
|
533
|
+
this.localCollection.add(interval);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
this.savedSerializedIntervals = undefined;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Gets the next local sequence number, modifying this client's collab window in doing so.
|
|
540
|
+
*/
|
|
541
|
+
getNextLocalSeq() {
|
|
542
|
+
if (this.client) {
|
|
543
|
+
return ++this.client.getCollabWindow().localSeq;
|
|
544
|
+
}
|
|
545
|
+
return 0;
|
|
546
|
+
}
|
|
547
|
+
emitChange(interval, previousInterval, local, slide, op) {
|
|
548
|
+
// Temporarily make references transient so that positional queries work (non-transient refs
|
|
549
|
+
// on resolve to DetachedPosition on any segments that don't contain them). The original refType
|
|
550
|
+
// is restored as single-endpoint changes re-use previous references.
|
|
551
|
+
let startRefType;
|
|
552
|
+
let endRefType;
|
|
553
|
+
if (previousInterval instanceof intervals_1.SequenceInterval) {
|
|
554
|
+
startRefType = previousInterval.start.refType;
|
|
555
|
+
endRefType = previousInterval.end.refType;
|
|
556
|
+
previousInterval.start.refType = merge_tree_1.ReferenceType.Transient;
|
|
557
|
+
previousInterval.end.refType = merge_tree_1.ReferenceType.Transient;
|
|
558
|
+
this.emit("changeInterval", interval, previousInterval, local, op, slide);
|
|
559
|
+
previousInterval.start.refType = startRefType;
|
|
560
|
+
previousInterval.end.refType = endRefType;
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
this.emit("changeInterval", interval, previousInterval, local, op, slide);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* {@inheritdoc IIntervalCollection.getIntervalById}
|
|
568
|
+
*/
|
|
569
|
+
getIntervalById(id) {
|
|
570
|
+
if (!this.localCollection) {
|
|
571
|
+
throw new telemetry_utils_1.LoggingError("attach must be called before accessing intervals");
|
|
572
|
+
}
|
|
573
|
+
return this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
574
|
+
}
|
|
575
|
+
assertStickinessEnabled(start, end) {
|
|
576
|
+
if (!(typeof start === "number" && typeof end === "number") &&
|
|
577
|
+
!this.options.intervalStickinessEnabled) {
|
|
578
|
+
throw new telemetry_utils_1.UsageError("attempted to set interval stickiness without enabling `intervalStickinessEnabled` feature flag");
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* {@inheritdoc IIntervalCollection.add}
|
|
583
|
+
*/
|
|
584
|
+
add({ start, end, props, }) {
|
|
585
|
+
if (!this.localCollection) {
|
|
586
|
+
throw new telemetry_utils_1.LoggingError("attach must be called prior to adding intervals");
|
|
587
|
+
}
|
|
588
|
+
const { startSide, endSide, startPos, endPos } = endpointPosAndSide(start, end);
|
|
589
|
+
(0, core_utils_1.assert)(startPos !== undefined &&
|
|
590
|
+
endPos !== undefined &&
|
|
591
|
+
startSide !== undefined &&
|
|
592
|
+
endSide !== undefined, 0x793 /* start and end cannot be undefined because they were not passed in as undefined */);
|
|
593
|
+
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
594
|
+
this.assertStickinessEnabled(start, end);
|
|
595
|
+
const interval = this.localCollection.addInterval(toSequencePlace(startPos, startSide), toSequencePlace(endPos, endSide), intervals_1.IntervalType.SlideOnRemove, props);
|
|
596
|
+
if (interval) {
|
|
597
|
+
if (!this.isCollaborating && interval instanceof intervals_1.SequenceInterval) {
|
|
598
|
+
setSlideOnRemove(interval.start);
|
|
599
|
+
setSlideOnRemove(interval.end);
|
|
600
|
+
}
|
|
601
|
+
const serializedInterval = {
|
|
602
|
+
start: startPos,
|
|
603
|
+
end: endPos,
|
|
604
|
+
intervalType: intervals_1.IntervalType.SlideOnRemove,
|
|
605
|
+
properties: interval.properties,
|
|
606
|
+
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
607
|
+
stickiness,
|
|
608
|
+
startSide,
|
|
609
|
+
endSide,
|
|
610
|
+
};
|
|
611
|
+
const localSeq = this.getNextLocalSeq();
|
|
612
|
+
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
613
|
+
// Local ops get submitted to the server. Remote ops have the deserializer run.
|
|
614
|
+
this.emitter.emit("add", undefined, serializedInterval, { localSeq });
|
|
615
|
+
}
|
|
616
|
+
this.emit("addInterval", interval, true, undefined);
|
|
617
|
+
return interval;
|
|
618
|
+
}
|
|
619
|
+
deleteExistingInterval(interval, local, op) {
|
|
620
|
+
if (!this.localCollection) {
|
|
621
|
+
throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
|
|
622
|
+
}
|
|
623
|
+
// The given interval is known to exist in the collection.
|
|
624
|
+
this.localCollection.removeExistingInterval(interval);
|
|
625
|
+
if (interval) {
|
|
626
|
+
// Local ops get submitted to the server. Remote ops have the deserializer run.
|
|
627
|
+
if (local) {
|
|
628
|
+
this.emitter.emit("delete", undefined, interval.serialize(), {
|
|
629
|
+
localSeq: this.getNextLocalSeq(),
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
if (this.onDeserialize) {
|
|
634
|
+
this.onDeserialize(interval);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
this.emit("deleteInterval", interval, local, op);
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* {@inheritdoc IIntervalCollection.removeIntervalById}
|
|
642
|
+
*/
|
|
643
|
+
removeIntervalById(id) {
|
|
644
|
+
if (!this.localCollection) {
|
|
645
|
+
throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
|
|
646
|
+
}
|
|
647
|
+
const interval = this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
648
|
+
if (interval) {
|
|
649
|
+
this.deleteExistingInterval(interval, true, undefined);
|
|
650
|
+
}
|
|
651
|
+
return interval;
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* {@inheritdoc IIntervalCollection.changeProperties}
|
|
655
|
+
* @deprecated - call change with the id and an object containing the new props values
|
|
656
|
+
*/
|
|
657
|
+
changeProperties(id, props) {
|
|
658
|
+
this.change(id, { props });
|
|
659
|
+
}
|
|
660
|
+
change(arg1, arg2, arg3) {
|
|
661
|
+
const id = arg1;
|
|
662
|
+
let start;
|
|
663
|
+
let end;
|
|
664
|
+
let props;
|
|
665
|
+
if (isSequencePlace(arg2)) {
|
|
666
|
+
start = arg2;
|
|
667
|
+
end = arg3;
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
start = arg2.start;
|
|
671
|
+
end = arg2.end;
|
|
672
|
+
props = arg2.props;
|
|
673
|
+
}
|
|
674
|
+
if (!this.localCollection) {
|
|
675
|
+
throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
|
|
676
|
+
}
|
|
677
|
+
// Force id to be a string.
|
|
678
|
+
if (typeof id !== "string") {
|
|
679
|
+
throw new telemetry_utils_1.UsageError("Change API requires an ID that is a string");
|
|
680
|
+
}
|
|
681
|
+
// Ensure that both start and end are defined or both are undefined.
|
|
682
|
+
if ((start === undefined) !== (end === undefined)) {
|
|
683
|
+
throw new telemetry_utils_1.UsageError("Change API requires both start and end to be defined or undefined");
|
|
684
|
+
}
|
|
685
|
+
// prevent the overwriting of an interval label, it should remain unchanged
|
|
686
|
+
// once it has been inserted into the collection.
|
|
687
|
+
if (props?.[merge_tree_1.reservedRangeLabelsKey] !== undefined) {
|
|
688
|
+
throw new telemetry_utils_1.UsageError("The label property should not be modified once inserted to the collection");
|
|
689
|
+
}
|
|
690
|
+
const interval = this.getIntervalById(id);
|
|
691
|
+
if (interval) {
|
|
692
|
+
let deltaProps;
|
|
693
|
+
let newInterval;
|
|
694
|
+
if (props !== undefined) {
|
|
695
|
+
deltaProps = interval.addProperties(props, true, this.isCollaborating ? merge_tree_1.UnassignedSequenceNumber : merge_tree_1.UniversalSequenceNumber);
|
|
696
|
+
}
|
|
697
|
+
if (start !== undefined && end !== undefined) {
|
|
698
|
+
newInterval = this.localCollection.changeInterval(interval, start, end);
|
|
699
|
+
if (!this.isCollaborating && newInterval instanceof intervals_1.SequenceInterval) {
|
|
700
|
+
setSlideOnRemove(newInterval.start);
|
|
701
|
+
setSlideOnRemove(newInterval.end);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
const serializedInterval = interval.serialize();
|
|
705
|
+
const { startPos, startSide, endPos, endSide } = endpointPosAndSide(start, end);
|
|
706
|
+
const stickiness = computeStickinessFromSide(startPos, startSide, endPos, endSide);
|
|
707
|
+
serializedInterval.start = startPos;
|
|
708
|
+
serializedInterval.end = endPos;
|
|
709
|
+
serializedInterval.startSide = startSide;
|
|
710
|
+
serializedInterval.endSide = endSide;
|
|
711
|
+
serializedInterval.stickiness = stickiness;
|
|
712
|
+
// Emit a property bag containing the ID and the other (if any) properties changed
|
|
713
|
+
serializedInterval.properties = {
|
|
714
|
+
[reservedIntervalIdKey]: interval.getIntervalId(),
|
|
715
|
+
...props,
|
|
716
|
+
};
|
|
717
|
+
const localSeq = this.getNextLocalSeq();
|
|
718
|
+
this.localSeqToSerializedInterval.set(localSeq, serializedInterval);
|
|
719
|
+
this.emitter.emit("change", undefined, serializedInterval, { localSeq });
|
|
720
|
+
if (deltaProps !== undefined) {
|
|
721
|
+
this.emit("propertyChanged", interval, deltaProps, true, undefined);
|
|
722
|
+
}
|
|
723
|
+
if (newInterval) {
|
|
724
|
+
this.addPendingChange(id, serializedInterval);
|
|
725
|
+
this.emitChange(newInterval, interval, true, false);
|
|
726
|
+
}
|
|
727
|
+
return newInterval;
|
|
728
|
+
}
|
|
729
|
+
// No interval to change
|
|
730
|
+
return undefined;
|
|
731
|
+
}
|
|
732
|
+
get isCollaborating() {
|
|
733
|
+
return this.client?.getCollabWindow().collaborating ?? false;
|
|
734
|
+
}
|
|
735
|
+
addPendingChange(id, serializedInterval) {
|
|
736
|
+
if (!this.isCollaborating) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
if (serializedInterval.start !== undefined) {
|
|
740
|
+
this.addPendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
741
|
+
}
|
|
742
|
+
if (serializedInterval.end !== undefined) {
|
|
743
|
+
this.addPendingChangeHelper(id, this.pendingChangesEnd, serializedInterval);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
addPendingChangeHelper(id, pendingChanges, serializedInterval) {
|
|
747
|
+
let entries = pendingChanges.get(id);
|
|
748
|
+
if (!entries) {
|
|
749
|
+
entries = [];
|
|
750
|
+
pendingChanges.set(id, entries);
|
|
751
|
+
}
|
|
752
|
+
entries.push(serializedInterval);
|
|
753
|
+
}
|
|
754
|
+
removePendingChange(serializedInterval) {
|
|
755
|
+
// Change ops always have an ID.
|
|
756
|
+
const id = serializedInterval.properties?.[reservedIntervalIdKey];
|
|
757
|
+
if (serializedInterval.start !== undefined) {
|
|
758
|
+
this.removePendingChangeHelper(id, this.pendingChangesStart, serializedInterval);
|
|
759
|
+
}
|
|
760
|
+
if (serializedInterval.end !== undefined) {
|
|
761
|
+
this.removePendingChangeHelper(id, this.pendingChangesEnd, serializedInterval);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
removePendingChangeHelper(id, pendingChanges, serializedInterval) {
|
|
765
|
+
const entries = pendingChanges.get(id);
|
|
766
|
+
if (entries) {
|
|
767
|
+
const pendingChange = entries.shift();
|
|
768
|
+
if (entries.length === 0) {
|
|
769
|
+
pendingChanges.delete(id);
|
|
770
|
+
}
|
|
771
|
+
if (pendingChange?.start !== serializedInterval.start ||
|
|
772
|
+
pendingChange?.end !== serializedInterval.end) {
|
|
773
|
+
throw new telemetry_utils_1.LoggingError("Mismatch in pending changes");
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
hasPendingChangeStart(id) {
|
|
778
|
+
const entries = this.pendingChangesStart.get(id);
|
|
779
|
+
return entries && entries.length !== 0;
|
|
780
|
+
}
|
|
781
|
+
hasPendingChangeEnd(id) {
|
|
782
|
+
const entries = this.pendingChangesEnd.get(id);
|
|
783
|
+
return entries && entries.length !== 0;
|
|
784
|
+
}
|
|
785
|
+
/** @internal */
|
|
786
|
+
ackChange(serializedInterval, local, op, localOpMetadata) {
|
|
787
|
+
if (!this.localCollection) {
|
|
788
|
+
throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
|
|
789
|
+
}
|
|
790
|
+
if (local) {
|
|
791
|
+
(0, core_utils_1.assert)(localOpMetadata !== undefined, 0x552 /* op metadata should be defined for local op */);
|
|
792
|
+
this.localSeqToSerializedInterval.delete(localOpMetadata?.localSeq);
|
|
793
|
+
// This is an ack from the server. Remove the pending change.
|
|
794
|
+
this.removePendingChange(serializedInterval);
|
|
795
|
+
}
|
|
796
|
+
// Note that the ID is in the property bag only to allow us to find the interval.
|
|
797
|
+
// This API cannot change the ID, and writing to the ID property will result in an exception. So we
|
|
798
|
+
// strip it out of the properties here.
|
|
799
|
+
const { [reservedIntervalIdKey]: id, ...newProps } = serializedInterval.properties ?? {};
|
|
800
|
+
(0, core_utils_1.assert)(id !== undefined, 0x3fe /* id must exist on the interval */);
|
|
801
|
+
const interval = this.getIntervalById(id);
|
|
802
|
+
if (!interval) {
|
|
803
|
+
// The interval has been removed locally; no-op.
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
if (local) {
|
|
807
|
+
// Let the propertyManager prune its pending change-properties set.
|
|
808
|
+
interval.propertyManager?.ackPendingProperties({
|
|
809
|
+
type: merge_tree_1.MergeTreeDeltaType.ANNOTATE,
|
|
810
|
+
props: serializedInterval.properties ?? {},
|
|
811
|
+
});
|
|
812
|
+
this.ackInterval(interval, op);
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
// If there are pending changes with this ID, don't apply the remote start/end change, as the local ack
|
|
816
|
+
// should be the winning change.
|
|
817
|
+
let start;
|
|
818
|
+
let end;
|
|
819
|
+
// Track pending start/end independently of one another.
|
|
820
|
+
if (!this.hasPendingChangeStart(id)) {
|
|
821
|
+
start = serializedInterval.start;
|
|
822
|
+
}
|
|
823
|
+
if (!this.hasPendingChangeEnd(id)) {
|
|
824
|
+
end = serializedInterval.end;
|
|
825
|
+
}
|
|
826
|
+
let newInterval = interval;
|
|
827
|
+
if (start !== undefined || end !== undefined) {
|
|
828
|
+
// If changeInterval gives us a new interval, work with that one. Otherwise keep working with
|
|
829
|
+
// the one we originally found in the tree.
|
|
830
|
+
newInterval =
|
|
831
|
+
this.localCollection.changeInterval(interval, toOptionalSequencePlace(start, serializedInterval.startSide), toOptionalSequencePlace(end, serializedInterval.endSide), op) ?? interval;
|
|
832
|
+
}
|
|
833
|
+
const deltaProps = newInterval.addProperties(newProps, true, op.sequenceNumber);
|
|
834
|
+
if (this.onDeserialize) {
|
|
835
|
+
this.onDeserialize(newInterval);
|
|
836
|
+
}
|
|
837
|
+
if (newInterval !== interval) {
|
|
838
|
+
this.emitChange(newInterval, interval, local, false, op);
|
|
839
|
+
}
|
|
840
|
+
const changedProperties = Object.keys(newProps).length > 0;
|
|
841
|
+
if (changedProperties) {
|
|
842
|
+
this.emit("propertyChanged", interval, deltaProps, local, op);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* {@inheritdoc IIntervalCollection.attachDeserializer}
|
|
848
|
+
*/
|
|
849
|
+
attachDeserializer(onDeserialize) {
|
|
850
|
+
// If no deserializer is specified can skip all processing work
|
|
851
|
+
if (!onDeserialize) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
// Start by storing the callbacks so that any subsequent modifications make use of them
|
|
855
|
+
this.onDeserialize = onDeserialize;
|
|
856
|
+
// Trigger the async prepare work across all values in the collection
|
|
857
|
+
if (this.attached) {
|
|
858
|
+
this.map(onDeserialize);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Returns new interval after rebasing. If undefined, the interval was
|
|
863
|
+
* deleted as a result of rebasing. This can occur if the interval applies
|
|
864
|
+
* to a range that no longer exists, and the interval was unable to slide.
|
|
865
|
+
*
|
|
866
|
+
* @internal
|
|
867
|
+
*/
|
|
868
|
+
rebaseLocalInterval(opName, serializedInterval, localSeq) {
|
|
869
|
+
if (!this.client) {
|
|
870
|
+
// If there's no associated mergeTree client, the originally submitted op is still correct.
|
|
871
|
+
return serializedInterval;
|
|
872
|
+
}
|
|
873
|
+
if (!this.attached) {
|
|
874
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
875
|
+
}
|
|
876
|
+
const { intervalType, properties, stickiness, startSide, endSide } = serializedInterval;
|
|
877
|
+
const { start: startRebased, end: endRebased } = this.localSeqToRebasedInterval.get(localSeq) ?? this.computeRebasedPositions(localSeq);
|
|
878
|
+
const intervalId = properties?.[reservedIntervalIdKey];
|
|
879
|
+
const localInterval = this.localCollection?.idIntervalIndex.getIntervalById(intervalId);
|
|
880
|
+
const rebased = {
|
|
881
|
+
start: startRebased,
|
|
882
|
+
end: endRebased,
|
|
883
|
+
intervalType,
|
|
884
|
+
sequenceNumber: this.client?.getCurrentSeq() ?? 0,
|
|
885
|
+
properties,
|
|
886
|
+
stickiness,
|
|
887
|
+
startSide,
|
|
888
|
+
endSide,
|
|
889
|
+
};
|
|
890
|
+
if (opName === "change" &&
|
|
891
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- ?? is not logically equivalent when .hasPendingChangeStart returns false.
|
|
892
|
+
(this.hasPendingChangeStart(intervalId) || this.hasPendingChangeEnd(intervalId))) {
|
|
893
|
+
this.removePendingChange(serializedInterval);
|
|
894
|
+
this.addPendingChange(intervalId, rebased);
|
|
895
|
+
}
|
|
896
|
+
// if the interval slid off the string, rebase the op to be a noop and delete the interval.
|
|
897
|
+
if (startRebased === merge_tree_1.DetachedReferencePosition ||
|
|
898
|
+
endRebased === merge_tree_1.DetachedReferencePosition) {
|
|
899
|
+
if (localInterval) {
|
|
900
|
+
this.localCollection?.removeExistingInterval(localInterval);
|
|
901
|
+
}
|
|
902
|
+
return undefined;
|
|
903
|
+
}
|
|
904
|
+
if (localInterval !== undefined) {
|
|
905
|
+
// we know we must be using `SequenceInterval` because `this.client` exists
|
|
906
|
+
(0, core_utils_1.assert)(localInterval instanceof intervals_1.SequenceInterval, 0x3a0 /* localInterval must be `SequenceInterval` when used with client */);
|
|
907
|
+
// The rebased op may place this interval's endpoints on different segments. Calling `changeInterval` here
|
|
908
|
+
// updates the local client's state to be consistent with the emitted op.
|
|
909
|
+
this.localCollection?.changeInterval(localInterval, toOptionalSequencePlace(startRebased, startSide), toOptionalSequencePlace(endRebased, endSide), undefined, localSeq);
|
|
910
|
+
}
|
|
911
|
+
return rebased;
|
|
912
|
+
}
|
|
913
|
+
applyStashedOp(op) {
|
|
914
|
+
let interval;
|
|
915
|
+
let props;
|
|
916
|
+
let intervalId;
|
|
917
|
+
switch (op.opName) {
|
|
918
|
+
case intervals_1.IntervalDeltaOpType.ADD: {
|
|
919
|
+
(0, core_utils_1.assert)(op.value.start !== undefined, "start is undefined");
|
|
920
|
+
(0, core_utils_1.assert)(op.value.end !== undefined, "end is undefined");
|
|
921
|
+
interval = this.add({
|
|
922
|
+
start: op.value.start,
|
|
923
|
+
end: op.value.end,
|
|
924
|
+
props: op.value.properties,
|
|
925
|
+
});
|
|
926
|
+
const metadata = {
|
|
927
|
+
localSeq: this.getNextLocalSeq(),
|
|
928
|
+
};
|
|
929
|
+
if (interval !== undefined) {
|
|
930
|
+
this.localSeqToSerializedInterval.set(metadata.localSeq, interval.serialize());
|
|
931
|
+
}
|
|
932
|
+
return metadata;
|
|
933
|
+
}
|
|
934
|
+
case intervals_1.IntervalDeltaOpType.DELETE:
|
|
935
|
+
this.removeIntervalById(op.value.properties?.intervalId);
|
|
936
|
+
return {
|
|
937
|
+
localSeq: this.getNextLocalSeq(),
|
|
938
|
+
};
|
|
939
|
+
case intervals_1.IntervalDeltaOpType.CHANGE: {
|
|
940
|
+
(0, core_utils_1.assert)(op.value.properties !== undefined, "properties is undefined");
|
|
941
|
+
({ intervalId, ...props } = op.value.properties);
|
|
942
|
+
interval = this.change(intervalId, {
|
|
943
|
+
start: op.value.start,
|
|
944
|
+
end: op.value.end,
|
|
945
|
+
props,
|
|
946
|
+
});
|
|
947
|
+
const metadata = {
|
|
948
|
+
localSeq: this.getNextLocalSeq(),
|
|
949
|
+
};
|
|
950
|
+
if (interval !== undefined) {
|
|
951
|
+
this.localSeqToSerializedInterval.set(metadata.localSeq, interval.serialize());
|
|
952
|
+
}
|
|
953
|
+
return metadata;
|
|
954
|
+
}
|
|
955
|
+
default:
|
|
956
|
+
(0, core_utils_1.unreachableCase)(op.opName, `Unknown interval op type: ${op.opName}`);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
getSlideToSegment(lref) {
|
|
960
|
+
if (!this.client) {
|
|
961
|
+
throw new telemetry_utils_1.LoggingError("client does not exist");
|
|
962
|
+
}
|
|
963
|
+
const segoff = { segment: lref.getSegment(), offset: lref.getOffset() };
|
|
964
|
+
if (segoff.segment?.localRefs?.has(lref) !== true) {
|
|
965
|
+
return undefined;
|
|
966
|
+
}
|
|
967
|
+
const newSegoff = (0, merge_tree_1.getSlideToSegoff)(segoff, undefined, this.options.mergeTreeReferencesCanSlideToEndpoint);
|
|
968
|
+
const value = segoff.segment === newSegoff.segment && segoff.offset === newSegoff.offset
|
|
969
|
+
? undefined
|
|
970
|
+
: newSegoff;
|
|
971
|
+
return value;
|
|
972
|
+
}
|
|
973
|
+
ackInterval(interval, op) {
|
|
974
|
+
// Only SequenceIntervals need potential sliding
|
|
975
|
+
if (!(interval instanceof intervals_1.SequenceInterval)) {
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
if (!(0, merge_tree_1.refTypeIncludesFlag)(interval.start, merge_tree_1.ReferenceType.StayOnRemove) &&
|
|
979
|
+
!(0, merge_tree_1.refTypeIncludesFlag)(interval.end, merge_tree_1.ReferenceType.StayOnRemove)) {
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
const newStart = this.getSlideToSegment(interval.start);
|
|
983
|
+
const newEnd = this.getSlideToSegment(interval.end);
|
|
984
|
+
const id = interval.properties[reservedIntervalIdKey];
|
|
985
|
+
const hasPendingStartChange = this.hasPendingChangeStart(id);
|
|
986
|
+
const hasPendingEndChange = this.hasPendingChangeEnd(id);
|
|
987
|
+
if (!hasPendingStartChange) {
|
|
988
|
+
setSlideOnRemove(interval.start);
|
|
989
|
+
}
|
|
990
|
+
if (!hasPendingEndChange) {
|
|
991
|
+
setSlideOnRemove(interval.end);
|
|
992
|
+
}
|
|
993
|
+
const needsStartUpdate = newStart !== undefined && !hasPendingStartChange;
|
|
994
|
+
const needsEndUpdate = newEnd !== undefined && !hasPendingEndChange;
|
|
995
|
+
if (needsStartUpdate || needsEndUpdate) {
|
|
996
|
+
if (!this.localCollection) {
|
|
997
|
+
throw new telemetry_utils_1.LoggingError("Attach must be called before accessing intervals");
|
|
998
|
+
}
|
|
999
|
+
// `interval`'s endpoints will get modified in-place, so clone it prior to doing so for event emission.
|
|
1000
|
+
const oldInterval = interval.clone();
|
|
1001
|
+
// In this case, where we change the start or end of an interval,
|
|
1002
|
+
// it is necessary to remove and re-add the interval listeners.
|
|
1003
|
+
// This ensures that the correct listeners are added to the LocalReferencePosition.
|
|
1004
|
+
this.localCollection.removeExistingInterval(interval);
|
|
1005
|
+
if (!this.client) {
|
|
1006
|
+
throw new telemetry_utils_1.LoggingError("client does not exist");
|
|
1007
|
+
}
|
|
1008
|
+
if (needsStartUpdate) {
|
|
1009
|
+
const props = interval.start.properties;
|
|
1010
|
+
interval.start = (0, intervals_1.createPositionReferenceFromSegoff)(this.client, newStart, interval.start.refType, op, undefined, undefined, (0, intervals_1.startReferenceSlidingPreference)(interval.stickiness), (0, intervals_1.startReferenceSlidingPreference)(interval.stickiness) ===
|
|
1011
|
+
merge_tree_1.SlidingPreference.BACKWARD);
|
|
1012
|
+
if (props) {
|
|
1013
|
+
interval.start.addProperties(props);
|
|
1014
|
+
}
|
|
1015
|
+
const oldSeg = oldInterval.start.getSegment();
|
|
1016
|
+
// remove and rebuild start interval as transient for event
|
|
1017
|
+
this.client.removeLocalReferencePosition(oldInterval.start);
|
|
1018
|
+
oldInterval.start.refType = merge_tree_1.ReferenceType.Transient;
|
|
1019
|
+
oldSeg?.localRefs?.addLocalRef(oldInterval.start, oldInterval.start.getOffset());
|
|
1020
|
+
}
|
|
1021
|
+
if (needsEndUpdate) {
|
|
1022
|
+
const props = interval.end.properties;
|
|
1023
|
+
interval.end = (0, intervals_1.createPositionReferenceFromSegoff)(this.client, newEnd, interval.end.refType, op, undefined, undefined, (0, intervals_1.endReferenceSlidingPreference)(interval.stickiness), (0, intervals_1.endReferenceSlidingPreference)(interval.stickiness) ===
|
|
1024
|
+
merge_tree_1.SlidingPreference.FORWARD);
|
|
1025
|
+
if (props) {
|
|
1026
|
+
interval.end.addProperties(props);
|
|
1027
|
+
}
|
|
1028
|
+
// remove and rebuild end interval as transient for event
|
|
1029
|
+
const oldSeg = oldInterval.end.getSegment();
|
|
1030
|
+
this.client.removeLocalReferencePosition(oldInterval.end);
|
|
1031
|
+
oldInterval.end.refType = merge_tree_1.ReferenceType.Transient;
|
|
1032
|
+
oldSeg?.localRefs?.addLocalRef(oldInterval.end, oldInterval.end.getOffset());
|
|
1033
|
+
}
|
|
1034
|
+
this.localCollection.add(interval);
|
|
1035
|
+
this.emitChange(interval, oldInterval, true, true, op);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
/** @internal */
|
|
1039
|
+
ackAdd(serializedInterval, local, op, localOpMetadata) {
|
|
1040
|
+
if (local) {
|
|
1041
|
+
(0, core_utils_1.assert)(localOpMetadata !== undefined, 0x553 /* op metadata should be defined for local op */);
|
|
1042
|
+
this.localSeqToSerializedInterval.delete(localOpMetadata.localSeq);
|
|
1043
|
+
const id = serializedInterval.properties?.[reservedIntervalIdKey];
|
|
1044
|
+
const localInterval = this.getIntervalById(id);
|
|
1045
|
+
if (localInterval) {
|
|
1046
|
+
this.ackInterval(localInterval, op);
|
|
1047
|
+
}
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
if (!this.localCollection) {
|
|
1051
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
1052
|
+
}
|
|
1053
|
+
this.localCollection.ensureSerializedId(serializedInterval);
|
|
1054
|
+
const interval = this.localCollection.addInterval(toSequencePlace(serializedInterval.start, serializedInterval.startSide ?? Side.Before), toSequencePlace(serializedInterval.end, serializedInterval.endSide ?? Side.Before), serializedInterval.intervalType, serializedInterval.properties, op);
|
|
1055
|
+
if (interval) {
|
|
1056
|
+
if (this.onDeserialize) {
|
|
1057
|
+
this.onDeserialize(interval);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
this.emit("addInterval", interval, local, op);
|
|
1061
|
+
return interval;
|
|
1062
|
+
}
|
|
1063
|
+
/** @internal */
|
|
1064
|
+
ackDelete(serializedInterval, local, op) {
|
|
1065
|
+
if (local) {
|
|
1066
|
+
// Local ops were applied when the message was created and there's no "pending delete"
|
|
1067
|
+
// state to book keep: remote operation application takes into account possibility of
|
|
1068
|
+
// locally deleted interval whenever a lookup happens.
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
if (!this.localCollection) {
|
|
1072
|
+
throw new telemetry_utils_1.LoggingError("attach must be called prior to deleting intervals");
|
|
1073
|
+
}
|
|
1074
|
+
const id = this.localCollection.ensureSerializedId(serializedInterval);
|
|
1075
|
+
const interval = this.localCollection.idIntervalIndex.getIntervalById(id);
|
|
1076
|
+
if (interval) {
|
|
1077
|
+
this.deleteExistingInterval(interval, local, op);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* @internal
|
|
1082
|
+
*/
|
|
1083
|
+
serializeInternal() {
|
|
1084
|
+
if (!this.localCollection) {
|
|
1085
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
1086
|
+
}
|
|
1087
|
+
return this.localCollection.serialize();
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* @returns an iterator over all intervals in this collection.
|
|
1091
|
+
*/
|
|
1092
|
+
[Symbol.iterator]() {
|
|
1093
|
+
const iterator = new IntervalCollectionIterator(this);
|
|
1094
|
+
return iterator;
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* {@inheritdoc IIntervalCollection.CreateForwardIteratorWithStartPosition}
|
|
1098
|
+
*/
|
|
1099
|
+
CreateForwardIteratorWithStartPosition(startPosition) {
|
|
1100
|
+
const iterator = new IntervalCollectionIterator(this, true, startPosition);
|
|
1101
|
+
return iterator;
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithStartPosition}
|
|
1105
|
+
*/
|
|
1106
|
+
CreateBackwardIteratorWithStartPosition(startPosition) {
|
|
1107
|
+
const iterator = new IntervalCollectionIterator(this, false, startPosition);
|
|
1108
|
+
return iterator;
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* {@inheritdoc IIntervalCollection.CreateForwardIteratorWithEndPosition}
|
|
1112
|
+
*/
|
|
1113
|
+
CreateForwardIteratorWithEndPosition(endPosition) {
|
|
1114
|
+
const iterator = new IntervalCollectionIterator(this, true, undefined, endPosition);
|
|
1115
|
+
return iterator;
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* {@inheritdoc IIntervalCollection.CreateBackwardIteratorWithEndPosition}
|
|
1119
|
+
*/
|
|
1120
|
+
CreateBackwardIteratorWithEndPosition(endPosition) {
|
|
1121
|
+
const iterator = new IntervalCollectionIterator(this, false, undefined, endPosition);
|
|
1122
|
+
return iterator;
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* {@inheritdoc IIntervalCollection.gatherIterationResults}
|
|
1126
|
+
*/
|
|
1127
|
+
gatherIterationResults(results, iteratesForward, start, end) {
|
|
1128
|
+
if (!this.localCollection) {
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
this.localCollection.overlappingIntervalsIndex.gatherIterationResults(results, iteratesForward, start, end);
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* {@inheritdoc IIntervalCollection.findOverlappingIntervals}
|
|
1135
|
+
*/
|
|
1136
|
+
findOverlappingIntervals(startPosition, endPosition) {
|
|
1137
|
+
if (!this.localCollection) {
|
|
1138
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
1139
|
+
}
|
|
1140
|
+
return this.localCollection.overlappingIntervalsIndex.findOverlappingIntervals(startPosition, endPosition);
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* {@inheritdoc IIntervalCollection.map}
|
|
1144
|
+
*/
|
|
1145
|
+
map(fn) {
|
|
1146
|
+
if (!this.localCollection) {
|
|
1147
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
1148
|
+
}
|
|
1149
|
+
for (const interval of this.localCollection.idIntervalIndex) {
|
|
1150
|
+
fn(interval);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* {@inheritdoc IIntervalCollection.previousInterval}
|
|
1155
|
+
*/
|
|
1156
|
+
previousInterval(pos) {
|
|
1157
|
+
if (!this.localCollection) {
|
|
1158
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
1159
|
+
}
|
|
1160
|
+
return this.localCollection.endIntervalIndex.previousInterval(pos);
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* {@inheritdoc IIntervalCollection.nextInterval}
|
|
1164
|
+
*/
|
|
1165
|
+
nextInterval(pos) {
|
|
1166
|
+
if (!this.localCollection) {
|
|
1167
|
+
throw new telemetry_utils_1.LoggingError("attachSequence must be called");
|
|
1168
|
+
}
|
|
1169
|
+
return this.localCollection.endIntervalIndex.nextInterval(pos);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
exports.IntervalCollection = IntervalCollection;
|
|
1173
|
+
function setSlideOnRemove(lref) {
|
|
1174
|
+
let refType = lref.refType;
|
|
1175
|
+
refType = refType & ~merge_tree_1.ReferenceType.StayOnRemove;
|
|
1176
|
+
refType = refType | merge_tree_1.ReferenceType.SlideOnRemove;
|
|
1177
|
+
lref.refType = refType;
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Returns an object that can be used to find the interval a given LocalReferencePosition belongs to.
|
|
1181
|
+
* @returns undefined if the reference position is not the endpoint of any interval (e.g. it was created
|
|
1182
|
+
* on the merge tree directly by app code), otherwise an {@link IntervalLocator} for the interval this
|
|
1183
|
+
* endpoint is a part of.
|
|
1184
|
+
* @internal
|
|
1185
|
+
*/
|
|
1186
|
+
function intervalLocatorFromEndpoint(potentialEndpoint) {
|
|
1187
|
+
const { interval, [merge_tree_1.reservedRangeLabelsKey]: collectionNameArray } = potentialEndpoint.properties ?? {};
|
|
1188
|
+
return interval && collectionNameArray?.length === 1
|
|
1189
|
+
? { label: collectionNameArray[0], interval }
|
|
1190
|
+
: undefined;
|
|
1191
|
+
}
|
|
1192
|
+
exports.intervalLocatorFromEndpoint = intervalLocatorFromEndpoint;
|
|
1193
|
+
//# sourceMappingURL=intervalCollection.cjs.map
|