@fluidframework/merge-tree 2.22.1 → 2.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +12 -9
- package/dist/mergeTree.js.map +1 -1
- package/dist/mergeTreeNodes.d.ts.map +1 -1
- package/dist/mergeTreeNodes.js +0 -2
- package/dist/mergeTreeNodes.js.map +1 -1
- package/dist/partialLengths.d.ts.map +1 -1
- package/dist/partialLengths.js +3 -4
- package/dist/partialLengths.js.map +1 -1
- package/dist/segmentInfos.d.ts +10 -18
- package/dist/segmentInfos.d.ts.map +1 -1
- package/dist/segmentInfos.js +22 -3
- package/dist/segmentInfos.js.map +1 -1
- package/dist/snapshotLoader.d.ts.map +1 -1
- package/dist/snapshotLoader.js +0 -2
- package/dist/snapshotLoader.js.map +1 -1
- package/dist/test/client.obliterateFarm.spec.d.ts.map +1 -1
- package/dist/test/client.obliterateFarm.spec.js +12 -0
- package/dist/test/client.obliterateFarm.spec.js.map +1 -1
- package/dist/test/obliterate.concurrent.spec.js +1 -1
- package/dist/test/obliterate.concurrent.spec.js.map +1 -1
- package/dist/test/obliterateOperations.d.ts +1 -0
- package/dist/test/obliterateOperations.d.ts.map +1 -1
- package/dist/test/obliterateOperations.js +35 -13
- package/dist/test/obliterateOperations.js.map +1 -1
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +13 -10
- package/lib/mergeTree.js.map +1 -1
- package/lib/mergeTreeNodes.d.ts.map +1 -1
- package/lib/mergeTreeNodes.js +0 -2
- package/lib/mergeTreeNodes.js.map +1 -1
- package/lib/partialLengths.d.ts.map +1 -1
- package/lib/partialLengths.js +4 -5
- package/lib/partialLengths.js.map +1 -1
- package/lib/segmentInfos.d.ts +10 -18
- package/lib/segmentInfos.d.ts.map +1 -1
- package/lib/segmentInfos.js +20 -2
- package/lib/segmentInfos.js.map +1 -1
- package/lib/snapshotLoader.d.ts.map +1 -1
- package/lib/snapshotLoader.js +0 -2
- package/lib/snapshotLoader.js.map +1 -1
- package/lib/test/client.obliterateFarm.spec.d.ts.map +1 -1
- package/lib/test/client.obliterateFarm.spec.js +13 -1
- package/lib/test/client.obliterateFarm.spec.js.map +1 -1
- package/lib/test/obliterate.concurrent.spec.js +1 -1
- package/lib/test/obliterate.concurrent.spec.js.map +1 -1
- package/lib/test/obliterateOperations.d.ts +1 -0
- package/lib/test/obliterateOperations.d.ts.map +1 -1
- package/lib/test/obliterateOperations.js +33 -12
- package/lib/test/obliterateOperations.js.map +1 -1
- package/package.json +17 -17
- package/src/mergeTree.ts +14 -9
- package/src/mergeTreeNodes.ts +0 -2
- package/src/partialLengths.ts +12 -5
- package/src/segmentInfos.ts +23 -21
- package/src/snapshotLoader.ts +0 -2
|
@@ -8,6 +8,7 @@ import { type TestOperation } from "./mergeTreeOperationRunner.js";
|
|
|
8
8
|
import type { TestClient } from "./testClient.js";
|
|
9
9
|
export declare const insertField: TestOperation;
|
|
10
10
|
export declare const obliterateField: TestOperation;
|
|
11
|
+
export declare const obliterateFieldZeroLength: TestOperation;
|
|
11
12
|
export declare const insertAvoidField: TestOperation;
|
|
12
13
|
export declare const removeWithField: TestOperation;
|
|
13
14
|
export declare const annotateWithField: TestOperation;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"obliterateOperations.d.ts","sourceRoot":"","sources":["../../src/test/obliterateOperations.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAGpE,OAAO,KAAK,EAAuB,YAAY,EAAE,MAAM,WAAW,CAAC;AAGnE,OAAO,EAA8B,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAuElD,eAAO,MAAM,WAAW,EAAE,aAUzB,CAAC;
|
|
1
|
+
{"version":3,"file":"obliterateOperations.d.ts","sourceRoot":"","sources":["../../src/test/obliterateOperations.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAGpE,OAAO,KAAK,EAAuB,YAAY,EAAE,MAAM,WAAW,CAAC;AAGnE,OAAO,EAA8B,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAuElD,eAAO,MAAM,WAAW,EAAE,aAUzB,CAAC;AAoBF,eAAO,MAAM,eAAe,EAAE,aAkC7B,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,aAgCvC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,aAY9B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,aAc7B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,aAc/B,CAAC;AAEF,eAAO,MAAM,uBAAuB,WAC3B,UAAU,UACV,OAAO,KACb,YAAY,GAAG,SAQjB,CAAC"}
|
|
@@ -51,6 +51,15 @@ export const insertField = (client, opStart, opEnd, random) => {
|
|
|
51
51
|
return client.insertTextLocal(opStart, `{${numberText}}`);
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
+
const obliterateHelper = (client, startPos, endPos, random) => {
|
|
55
|
+
const obliterateOp = client.obliterateRangeLocal({ pos: startPos, side: Side.After }, { pos: endPos, side: Side.Before });
|
|
56
|
+
const insertOp = insertFieldText(client, startPos + 1, random);
|
|
57
|
+
assert(insertOp !== undefined, "Insert op should not be undefined");
|
|
58
|
+
// TODO: AB#31001: We should be able to sometimes use group ops here rather than submit two separate ops,
|
|
59
|
+
// but this causes failures which likely indicate there are bugs with the intersection of obliterate and grouped batching.
|
|
60
|
+
// const op = createGroupOp(obliterateOp, insertOp);
|
|
61
|
+
return [obliterateOp, insertOp];
|
|
62
|
+
};
|
|
54
63
|
export const obliterateField = (client, opStart, opEnd, random) => {
|
|
55
64
|
const fieldEndpoints = getFieldEndpoints(client, opStart,
|
|
56
65
|
// the operation runner generates endpoints with client length, but this model only supports up to client length - 1.
|
|
@@ -59,18 +68,30 @@ export const obliterateField = (client, opStart, opEnd, random) => {
|
|
|
59
68
|
if (fieldEndpoints !== undefined) {
|
|
60
69
|
const { startPos, endPos } = fieldEndpoints;
|
|
61
70
|
// Obliterate text between the separators, but avoid the case where the obliterate range is zero length.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
return endPos - startPos > 1
|
|
72
|
+
? obliterateHelper(client, startPos, endPos, random)
|
|
73
|
+
: undefined;
|
|
74
|
+
}
|
|
75
|
+
if (opEnd >= client.getLength()) {
|
|
76
|
+
endISP = { pos: client.getLength() - 1, side: Side.After };
|
|
77
|
+
}
|
|
78
|
+
if (!client.getText(opStart, opEnd).includes("{")) {
|
|
79
|
+
// Avoid issuing obliterates that might contain multiple fields.
|
|
80
|
+
// Otherwise we may end up with field characters that look like they're outside of the field,
|
|
81
|
+
// since one of these obliterates can wipe out the field including the `{}` delimiters, but
|
|
82
|
+
// a "field replace" obliterate + insert can win and insert the numerical characters.
|
|
83
|
+
return client.obliterateRangeLocal({ pos: opStart, side: Side.Before }, endISP ?? { pos: opEnd, side: Side.After });
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
export const obliterateFieldZeroLength = (client, opStart, opEnd, random) => {
|
|
87
|
+
const fieldEndpoints = getFieldEndpoints(client, opStart,
|
|
88
|
+
// the operation runner generates endpoints with client length, but this model only supports up to client length - 1.
|
|
89
|
+
Math.min(opEnd, client.getLength() - 1));
|
|
90
|
+
let endISP;
|
|
91
|
+
if (fieldEndpoints !== undefined) {
|
|
92
|
+
const { startPos, endPos } = fieldEndpoints;
|
|
93
|
+
// Obliterate text between the separators, including the case where the obliterate range is zero length.
|
|
94
|
+
return obliterateHelper(client, startPos, endPos, random);
|
|
74
95
|
}
|
|
75
96
|
if (opEnd >= client.getLength()) {
|
|
76
97
|
endISP = { pos: client.getLength() - 1, side: Side.After };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"obliterateOperations.js","sourceRoot":"","sources":["../../src/test/obliterateOperations.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/* eslint-disable @typescript-eslint/no-non-null-assertion */\n\nimport { strict as assert } from \"node:assert\";\n\nimport type { IRandom } from \"@fluid-private/stochastic-test-utils\";\n\n// import { createGroupOp } from \"../opBuilder.js\";\nimport type { IMergeTreeInsertMsg, IMergeTreeOp } from \"../ops.js\";\nimport { InteriorSequencePlace, Side } from \"../sequencePlace.js\";\n\nimport { annotateRange, removeRange, type TestOperation } from \"./mergeTreeOperationRunner.js\";\nimport type { TestClient } from \"./testClient.js\";\n\nconst posInField = (\n\tclient: TestClient,\n\tpos: number,\n): { startPos: number; endPos: number } | undefined => {\n\tconst isFieldCharacter = (char: string): boolean =>\n\t\tNumber.isInteger(Number(char)) || char === \"{\" || char === \"}\";\n\tif (pos >= client.getLength() || !isFieldCharacter(client.getText(pos, pos + 1))) {\n\t\t// pos is not within a field.\n\t\treturn undefined;\n\t}\n\n\tlet startPos = pos;\n\tlet endPos = pos;\n\t// To find the start and end separators, walk backwards and forwards until the desired character is found.\n\twhile (startPos > 0 && client.getText(startPos, startPos + 1) !== \"{\") {\n\t\tconst text = client.getText(endPos, endPos + 1);\n\t\tassert(\n\t\t\tNumber.isInteger(Number(text)) || text === \"}\",\n\t\t\t\"Non-integer character found within a field\",\n\t\t);\n\t\tstartPos--;\n\t}\n\n\twhile (endPos < client.getLength() && client.getText(endPos, endPos + 1) !== \"}\") {\n\t\tconst text = client.getText(endPos, endPos + 1);\n\t\tassert(\n\t\t\tNumber.isInteger(Number(text)) || text === \"{\",\n\t\t\t\"Non-integer character found within a field\",\n\t\t);\n\t\tendPos++;\n\t}\n\n\tassert(client.getText(startPos, startPos + 1) === \"{\", \"Start separator not found\");\n\tassert(\n\t\tendPos < client.getLength() && client.getText(endPos, endPos + 1) === \"}\",\n\t\t\"End separator not found\",\n\t);\n\n\treturn { startPos, endPos };\n};\n\nconst getFieldEndpoints = (\n\tclient: TestClient,\n\tstart: number,\n\tend: number,\n): { startPos: number; endPos: number } | undefined => {\n\tconst startField = posInField(client, start);\n\tconst endField = posInField(client, end);\n\n\tif (startField === undefined && endField === undefined) {\n\t\treturn undefined;\n\t}\n\treturn startField ?? endField;\n};\n\nconst generateFieldText = (client: TestClient, random: IRandom): string => {\n\tconst chunkLength = random.integer(1, 10);\n\treturn (client.longClientId!.codePointAt(0)! % 10).toString().repeat(chunkLength);\n};\n\nconst insertFieldText = (\n\tclient: TestClient,\n\topStart: number,\n\trandom: IRandom,\n): IMergeTreeInsertMsg | undefined => {\n\tconst text = generateFieldText(client, random);\n\treturn client.insertTextLocal(opStart, text);\n};\n\nexport const insertField: TestOperation = (\n\tclient: TestClient,\n\topStart: number,\n\topEnd: number,\n\trandom: IRandom,\n) => {\n\tconst numberText = generateFieldText(client, random);\n\tif (posInField(client, opStart) === undefined) {\n\t\treturn client.insertTextLocal(opStart, `{${numberText}}`);\n\t}\n};\n\nexport const obliterateField: TestOperation = (\n\tclient: TestClient,\n\topStart: number,\n\topEnd: number,\n\trandom: IRandom,\n) => {\n\tconst fieldEndpoints = getFieldEndpoints(\n\t\tclient,\n\t\topStart,\n\t\t// the operation runner generates endpoints with client length, but this model only supports up to client length - 1.\n\t\tMath.min(opEnd, client.getLength() - 1),\n\t);\n\n\tlet endISP: InteriorSequencePlace | undefined;\n\tif (fieldEndpoints !== undefined) {\n\t\tconst { startPos, endPos } = fieldEndpoints;\n\t\t// Obliterate text between the separators, but avoid the case where the obliterate range is zero length.\n\t\tif (endPos - startPos > 1) {\n\t\t\tconst obliterateOp = client.obliterateRangeLocal(\n\t\t\t\t{ pos: startPos, side: Side.After },\n\t\t\t\t{ pos: endPos, side: Side.Before },\n\t\t\t);\n\t\t\tconst insertOp = insertFieldText(client, startPos + 1, random);\n\t\t\tassert(insertOp !== undefined, \"Insert op should not be undefined\");\n\t\t\t// TODO: AB#31001: We should be able to sometimes use group ops here rather than submit two separate ops,\n\t\t\t// but this causes failures which likely indicate there are bugs with the intersection of obliterate and grouped batching.\n\t\t\t// const op = createGroupOp(obliterateOp, insertOp);\n\t\t\treturn [obliterateOp, insertOp];\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\t}\n\tif (opEnd >= client.getLength()) {\n\t\tendISP = { pos: client.getLength() - 1, side: Side.After };\n\t}\n\tif (!client.getText(opStart, opEnd).includes(\"{\")) {\n\t\t// Avoid issuing obliterates that might contain multiple fields.\n\t\t// Otherwise we may end up with field characters that look like they're outside of the field,\n\t\t// since one of these obliterates can wipe out the field including the `{}` delimiters, but\n\t\t// a \"field replace\" obliterate + insert can win and insert the numerical characters.\n\t\treturn client.obliterateRangeLocal(\n\t\t\t{ pos: opStart, side: Side.Before },\n\t\t\tendISP ?? { pos: opEnd, side: Side.After },\n\t\t);\n\t}\n};\n\nexport const insertAvoidField: TestOperation = (\n\tclient: TestClient,\n\topStart: number,\n\topEnd: number,\n\trandom: IRandom,\n) => {\n\tlet start = opStart;\n\tconst endpoints = posInField(client, opStart);\n\tif (endpoints !== undefined) {\n\t\tstart = endpoints.startPos;\n\t}\n\treturn client.insertTextLocal(start, client.longClientId!.repeat(random.integer(1, 3)));\n};\n\nexport const removeWithField: TestOperation = (\n\tclient: TestClient,\n\topStart: number,\n\topEnd: number,\n\trandom: IRandom,\n) => {\n\tlet start = opStart;\n\tlet end = opEnd;\n\tconst fieldEndpoints = getFieldEndpoints(client, opStart, opEnd);\n\tif (fieldEndpoints !== undefined) {\n\t\tstart = fieldEndpoints.startPos;\n\t\tend = fieldEndpoints.endPos + 1;\n\t}\n\treturn removeRange(client, start, end, random);\n};\n\nexport const annotateWithField: TestOperation = (\n\tclient: TestClient,\n\topStart: number,\n\topEnd: number,\n\trandom: IRandom,\n) => {\n\tlet start = opStart;\n\tlet end = opEnd;\n\tconst fieldEndpoints = getFieldEndpoints(client, opStart, opEnd);\n\tif (fieldEndpoints !== undefined) {\n\t\tstart = fieldEndpoints.startPos;\n\t\tend = fieldEndpoints.endPos + 1;\n\t}\n\treturn annotateRange(client, start, end, random);\n};\n\nexport const generateInsertWithField = (\n\tclient: TestClient,\n\trandom: IRandom,\n): IMergeTreeOp | undefined => {\n\tconst text = client.longClientId!.repeat(random.integer(1, 3));\n\tlet pos = random.integer(0, client.getLength());\n\tconst endpoints = posInField(client, pos);\n\tif (endpoints !== undefined) {\n\t\tpos = 0;\n\t}\n\treturn client.insertTextLocal(pos, text);\n};\n"]}
|
|
1
|
+
{"version":3,"file":"obliterateOperations.js","sourceRoot":"","sources":["../../src/test/obliterateOperations.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/* eslint-disable @typescript-eslint/no-non-null-assertion */\n\nimport { strict as assert } from \"node:assert\";\n\nimport type { IRandom } from \"@fluid-private/stochastic-test-utils\";\n\n// import { createGroupOp } from \"../opBuilder.js\";\nimport type { IMergeTreeInsertMsg, IMergeTreeOp } from \"../ops.js\";\nimport { InteriorSequencePlace, Side } from \"../sequencePlace.js\";\n\nimport { annotateRange, removeRange, type TestOperation } from \"./mergeTreeOperationRunner.js\";\nimport type { TestClient } from \"./testClient.js\";\n\nconst posInField = (\n\tclient: TestClient,\n\tpos: number,\n): { startPos: number; endPos: number } | undefined => {\n\tconst isFieldCharacter = (char: string): boolean =>\n\t\tNumber.isInteger(Number(char)) || char === \"{\" || char === \"}\";\n\tif (pos >= client.getLength() || !isFieldCharacter(client.getText(pos, pos + 1))) {\n\t\t// pos is not within a field.\n\t\treturn undefined;\n\t}\n\n\tlet startPos = pos;\n\tlet endPos = pos;\n\t// To find the start and end separators, walk backwards and forwards until the desired character is found.\n\twhile (startPos > 0 && client.getText(startPos, startPos + 1) !== \"{\") {\n\t\tconst text = client.getText(endPos, endPos + 1);\n\t\tassert(\n\t\t\tNumber.isInteger(Number(text)) || text === \"}\",\n\t\t\t\"Non-integer character found within a field\",\n\t\t);\n\t\tstartPos--;\n\t}\n\n\twhile (endPos < client.getLength() && client.getText(endPos, endPos + 1) !== \"}\") {\n\t\tconst text = client.getText(endPos, endPos + 1);\n\t\tassert(\n\t\t\tNumber.isInteger(Number(text)) || text === \"{\",\n\t\t\t\"Non-integer character found within a field\",\n\t\t);\n\t\tendPos++;\n\t}\n\n\tassert(client.getText(startPos, startPos + 1) === \"{\", \"Start separator not found\");\n\tassert(\n\t\tendPos < client.getLength() && client.getText(endPos, endPos + 1) === \"}\",\n\t\t\"End separator not found\",\n\t);\n\n\treturn { startPos, endPos };\n};\n\nconst getFieldEndpoints = (\n\tclient: TestClient,\n\tstart: number,\n\tend: number,\n): { startPos: number; endPos: number } | undefined => {\n\tconst startField = posInField(client, start);\n\tconst endField = posInField(client, end);\n\n\tif (startField === undefined && endField === undefined) {\n\t\treturn undefined;\n\t}\n\treturn startField ?? endField;\n};\n\nconst generateFieldText = (client: TestClient, random: IRandom): string => {\n\tconst chunkLength = random.integer(1, 10);\n\treturn (client.longClientId!.codePointAt(0)! % 10).toString().repeat(chunkLength);\n};\n\nconst insertFieldText = (\n\tclient: TestClient,\n\topStart: number,\n\trandom: IRandom,\n): IMergeTreeInsertMsg | undefined => {\n\tconst text = generateFieldText(client, random);\n\treturn client.insertTextLocal(opStart, text);\n};\n\nexport const insertField: TestOperation = (\n\tclient: TestClient,\n\topStart: number,\n\topEnd: number,\n\trandom: IRandom,\n) => {\n\tconst numberText = generateFieldText(client, random);\n\tif (posInField(client, opStart) === undefined) {\n\t\treturn client.insertTextLocal(opStart, `{${numberText}}`);\n\t}\n};\n\nconst obliterateHelper = (\n\tclient: TestClient,\n\tstartPos: number,\n\tendPos: number,\n\trandom: IRandom,\n): IMergeTreeOp[] => {\n\tconst obliterateOp = client.obliterateRangeLocal(\n\t\t{ pos: startPos, side: Side.After },\n\t\t{ pos: endPos, side: Side.Before },\n\t);\n\tconst insertOp = insertFieldText(client, startPos + 1, random);\n\tassert(insertOp !== undefined, \"Insert op should not be undefined\");\n\t// TODO: AB#31001: We should be able to sometimes use group ops here rather than submit two separate ops,\n\t// but this causes failures which likely indicate there are bugs with the intersection of obliterate and grouped batching.\n\t// const op = createGroupOp(obliterateOp, insertOp);\n\treturn [obliterateOp, insertOp];\n};\n\nexport const obliterateField: TestOperation = (\n\tclient: TestClient,\n\topStart: number,\n\topEnd: number,\n\trandom: IRandom,\n) => {\n\tconst fieldEndpoints = getFieldEndpoints(\n\t\tclient,\n\t\topStart,\n\t\t// the operation runner generates endpoints with client length, but this model only supports up to client length - 1.\n\t\tMath.min(opEnd, client.getLength() - 1),\n\t);\n\n\tlet endISP: InteriorSequencePlace | undefined;\n\tif (fieldEndpoints !== undefined) {\n\t\tconst { startPos, endPos } = fieldEndpoints;\n\t\t// Obliterate text between the separators, but avoid the case where the obliterate range is zero length.\n\t\treturn endPos - startPos > 1\n\t\t\t? obliterateHelper(client, startPos, endPos, random)\n\t\t\t: undefined;\n\t}\n\tif (opEnd >= client.getLength()) {\n\t\tendISP = { pos: client.getLength() - 1, side: Side.After };\n\t}\n\tif (!client.getText(opStart, opEnd).includes(\"{\")) {\n\t\t// Avoid issuing obliterates that might contain multiple fields.\n\t\t// Otherwise we may end up with field characters that look like they're outside of the field,\n\t\t// since one of these obliterates can wipe out the field including the `{}` delimiters, but\n\t\t// a \"field replace\" obliterate + insert can win and insert the numerical characters.\n\t\treturn client.obliterateRangeLocal(\n\t\t\t{ pos: opStart, side: Side.Before },\n\t\t\tendISP ?? { pos: opEnd, side: Side.After },\n\t\t);\n\t}\n};\n\nexport const obliterateFieldZeroLength: TestOperation = (\n\tclient: TestClient,\n\topStart: number,\n\topEnd: number,\n\trandom: IRandom,\n) => {\n\tconst fieldEndpoints = getFieldEndpoints(\n\t\tclient,\n\t\topStart,\n\t\t// the operation runner generates endpoints with client length, but this model only supports up to client length - 1.\n\t\tMath.min(opEnd, client.getLength() - 1),\n\t);\n\n\tlet endISP: InteriorSequencePlace | undefined;\n\tif (fieldEndpoints !== undefined) {\n\t\tconst { startPos, endPos } = fieldEndpoints;\n\t\t// Obliterate text between the separators, including the case where the obliterate range is zero length.\n\t\treturn obliterateHelper(client, startPos, endPos, random);\n\t}\n\tif (opEnd >= client.getLength()) {\n\t\tendISP = { pos: client.getLength() - 1, side: Side.After };\n\t}\n\tif (!client.getText(opStart, opEnd).includes(\"{\")) {\n\t\t// Avoid issuing obliterates that might contain multiple fields.\n\t\t// Otherwise we may end up with field characters that look like they're outside of the field,\n\t\t// since one of these obliterates can wipe out the field including the `{}` delimiters, but\n\t\t// a \"field replace\" obliterate + insert can win and insert the numerical characters.\n\t\treturn client.obliterateRangeLocal(\n\t\t\t{ pos: opStart, side: Side.Before },\n\t\t\tendISP ?? { pos: opEnd, side: Side.After },\n\t\t);\n\t}\n};\n\nexport const insertAvoidField: TestOperation = (\n\tclient: TestClient,\n\topStart: number,\n\topEnd: number,\n\trandom: IRandom,\n) => {\n\tlet start = opStart;\n\tconst endpoints = posInField(client, opStart);\n\tif (endpoints !== undefined) {\n\t\tstart = endpoints.startPos;\n\t}\n\treturn client.insertTextLocal(start, client.longClientId!.repeat(random.integer(1, 3)));\n};\n\nexport const removeWithField: TestOperation = (\n\tclient: TestClient,\n\topStart: number,\n\topEnd: number,\n\trandom: IRandom,\n) => {\n\tlet start = opStart;\n\tlet end = opEnd;\n\tconst fieldEndpoints = getFieldEndpoints(client, opStart, opEnd);\n\tif (fieldEndpoints !== undefined) {\n\t\tstart = fieldEndpoints.startPos;\n\t\tend = fieldEndpoints.endPos + 1;\n\t}\n\treturn removeRange(client, start, end, random);\n};\n\nexport const annotateWithField: TestOperation = (\n\tclient: TestClient,\n\topStart: number,\n\topEnd: number,\n\trandom: IRandom,\n) => {\n\tlet start = opStart;\n\tlet end = opEnd;\n\tconst fieldEndpoints = getFieldEndpoints(client, opStart, opEnd);\n\tif (fieldEndpoints !== undefined) {\n\t\tstart = fieldEndpoints.startPos;\n\t\tend = fieldEndpoints.endPos + 1;\n\t}\n\treturn annotateRange(client, start, end, random);\n};\n\nexport const generateInsertWithField = (\n\tclient: TestClient,\n\trandom: IRandom,\n): IMergeTreeOp | undefined => {\n\tconst text = client.longClientId!.repeat(random.integer(1, 3));\n\tlet pos = random.integer(0, client.getLength());\n\tconst endpoints = posInField(client, pos);\n\tif (endpoints !== undefined) {\n\t\tpos = 0;\n\t}\n\treturn client.insertTextLocal(pos, text);\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/merge-tree",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.23.0",
|
|
4
4
|
"description": "Merge tree",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -81,30 +81,30 @@
|
|
|
81
81
|
"temp-directory": "nyc/.nyc_output"
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
|
-
"@fluid-internal/client-utils": "~2.
|
|
85
|
-
"@fluidframework/container-definitions": "~2.
|
|
86
|
-
"@fluidframework/core-interfaces": "~2.
|
|
87
|
-
"@fluidframework/core-utils": "~2.
|
|
88
|
-
"@fluidframework/datastore-definitions": "~2.
|
|
89
|
-
"@fluidframework/driver-definitions": "~2.
|
|
90
|
-
"@fluidframework/runtime-definitions": "~2.
|
|
91
|
-
"@fluidframework/runtime-utils": "~2.
|
|
92
|
-
"@fluidframework/shared-object-base": "~2.
|
|
93
|
-
"@fluidframework/telemetry-utils": "~2.
|
|
84
|
+
"@fluid-internal/client-utils": "~2.23.0",
|
|
85
|
+
"@fluidframework/container-definitions": "~2.23.0",
|
|
86
|
+
"@fluidframework/core-interfaces": "~2.23.0",
|
|
87
|
+
"@fluidframework/core-utils": "~2.23.0",
|
|
88
|
+
"@fluidframework/datastore-definitions": "~2.23.0",
|
|
89
|
+
"@fluidframework/driver-definitions": "~2.23.0",
|
|
90
|
+
"@fluidframework/runtime-definitions": "~2.23.0",
|
|
91
|
+
"@fluidframework/runtime-utils": "~2.23.0",
|
|
92
|
+
"@fluidframework/shared-object-base": "~2.23.0",
|
|
93
|
+
"@fluidframework/telemetry-utils": "~2.23.0"
|
|
94
94
|
},
|
|
95
95
|
"devDependencies": {
|
|
96
96
|
"@arethetypeswrong/cli": "^0.17.1",
|
|
97
97
|
"@biomejs/biome": "~1.9.3",
|
|
98
|
-
"@fluid-internal/mocha-test-setup": "~2.
|
|
99
|
-
"@fluid-private/stochastic-test-utils": "~2.
|
|
100
|
-
"@fluid-private/test-pairwise-generator": "~2.
|
|
98
|
+
"@fluid-internal/mocha-test-setup": "~2.23.0",
|
|
99
|
+
"@fluid-private/stochastic-test-utils": "~2.23.0",
|
|
100
|
+
"@fluid-private/test-pairwise-generator": "~2.23.0",
|
|
101
101
|
"@fluid-tools/benchmark": "^0.50.0",
|
|
102
|
-
"@fluid-tools/build-cli": "^0.
|
|
102
|
+
"@fluid-tools/build-cli": "^0.54.0",
|
|
103
103
|
"@fluidframework/build-common": "^2.0.3",
|
|
104
|
-
"@fluidframework/build-tools": "^0.
|
|
104
|
+
"@fluidframework/build-tools": "^0.54.0",
|
|
105
105
|
"@fluidframework/eslint-config-fluid": "^5.7.3",
|
|
106
106
|
"@fluidframework/merge-tree-previous": "npm:@fluidframework/merge-tree@2.22.0",
|
|
107
|
-
"@fluidframework/test-runtime-utils": "~2.
|
|
107
|
+
"@fluidframework/test-runtime-utils": "~2.23.0",
|
|
108
108
|
"@microsoft/api-extractor": "7.47.8",
|
|
109
109
|
"@types/diff": "^3.5.1",
|
|
110
110
|
"@types/mocha": "^10.0.10",
|
package/src/mergeTree.ts
CHANGED
|
@@ -95,6 +95,7 @@ import {
|
|
|
95
95
|
removeRemovalInfo,
|
|
96
96
|
toMoveInfo,
|
|
97
97
|
toRemovalInfo,
|
|
98
|
+
wasMovedOnInsert,
|
|
98
99
|
type IInsertionInfo,
|
|
99
100
|
type IMoveInfo,
|
|
100
101
|
type IRemovalInfo,
|
|
@@ -1083,7 +1084,14 @@ export class MergeTree {
|
|
|
1083
1084
|
if (!this.collabWindow.collaborating || this.collabWindow.clientId === clientId) {
|
|
1084
1085
|
if (node.isLeaf()) {
|
|
1085
1086
|
return this.localNetLength(node, refSeq, localSeq);
|
|
1086
|
-
} else if (
|
|
1087
|
+
} else if (
|
|
1088
|
+
localSeq === undefined ||
|
|
1089
|
+
// All changes are visible. Small note on why we allow refSeq >= this.collabWindow.currentSeq rather than just equality:
|
|
1090
|
+
// merge-tree eventing occurs before the collab window is updated to account for whatever op it is processing, and we want
|
|
1091
|
+
// to support resolving positions from within the event handler which account for that op. e.g. undo-redo relies on this
|
|
1092
|
+
// behavior with local references.
|
|
1093
|
+
(localSeq === this.collabWindow.localSeq && refSeq >= this.collabWindow.currentSeq)
|
|
1094
|
+
) {
|
|
1087
1095
|
// Local client sees all segments, even when collaborating
|
|
1088
1096
|
return node.cachedLength;
|
|
1089
1097
|
} else {
|
|
@@ -1178,6 +1186,8 @@ export class MergeTree {
|
|
|
1178
1186
|
*/
|
|
1179
1187
|
public referencePositionToLocalPosition(
|
|
1180
1188
|
refPos: ReferencePosition,
|
|
1189
|
+
// Note: this is not `this.collabWindow.currentSeq` because we want to support resolving local reference positions to positions
|
|
1190
|
+
// from within event handlers, and the collab window's sequence numbers are not updated in time in all of those cases.
|
|
1181
1191
|
refSeq = Number.MAX_SAFE_INTEGER,
|
|
1182
1192
|
clientId = this.collabWindow.clientId,
|
|
1183
1193
|
localSeq: number | undefined = this.collabWindow.localSeq,
|
|
@@ -1675,12 +1685,11 @@ export class MergeTree {
|
|
|
1675
1685
|
movedSeq: oldest.seq,
|
|
1676
1686
|
movedSeqs,
|
|
1677
1687
|
localMovedSeq: oldestUnacked?.localSeq,
|
|
1678
|
-
wasMovedOnInsert: oldest.seq !== UnassignedSequenceNumber,
|
|
1679
1688
|
};
|
|
1680
1689
|
} else {
|
|
1681
1690
|
assert(
|
|
1682
1691
|
oldestUnacked !== undefined,
|
|
1683
|
-
|
|
1692
|
+
0xb55 /* Expected local obliterate to be defined if newestAcked is not equal to newest */,
|
|
1684
1693
|
);
|
|
1685
1694
|
// There's a pending local obliterate for this range, so it will be marked as obliterated by us. However,
|
|
1686
1695
|
// all other clients are under the impression that the most recent acked obliterate won the right to insert
|
|
@@ -1690,7 +1699,6 @@ export class MergeTree {
|
|
|
1690
1699
|
movedSeq: oldestUnacked.seq,
|
|
1691
1700
|
movedSeqs: [oldestUnacked.seq],
|
|
1692
1701
|
localMovedSeq: oldestUnacked.localSeq,
|
|
1693
|
-
wasMovedOnInsert: false,
|
|
1694
1702
|
};
|
|
1695
1703
|
}
|
|
1696
1704
|
|
|
@@ -2141,8 +2149,6 @@ export class MergeTree {
|
|
|
2141
2149
|
movedSeq: seq,
|
|
2142
2150
|
localMovedSeq: localSeq,
|
|
2143
2151
|
movedSeqs: [seq],
|
|
2144
|
-
wasMovedOnInsert:
|
|
2145
|
-
segment.seq === UnassignedSequenceNumber && seq !== UnassignedSequenceNumber,
|
|
2146
2152
|
});
|
|
2147
2153
|
|
|
2148
2154
|
const existingRemoval = toRemovalInfo(movedSeg);
|
|
@@ -2158,16 +2164,15 @@ export class MergeTree {
|
|
|
2158
2164
|
}
|
|
2159
2165
|
} else {
|
|
2160
2166
|
if (existingMoveInfo.movedSeq === UnassignedSequenceNumber) {
|
|
2161
|
-
// Should not need explicit set here, but this should be implied:
|
|
2162
2167
|
assert(
|
|
2163
|
-
!
|
|
2168
|
+
!wasMovedOnInsert(segment),
|
|
2164
2169
|
0xab4 /* Local obliterate cannot have removed a segment as soon as it was inserted */,
|
|
2165
2170
|
);
|
|
2166
2171
|
assert(
|
|
2167
2172
|
seq !== UnassignedSequenceNumber,
|
|
2168
2173
|
0xab5 /* Cannot obliterate the same segment locally twice */,
|
|
2169
2174
|
);
|
|
2170
|
-
|
|
2175
|
+
|
|
2171
2176
|
// we moved this locally, but someone else moved it first
|
|
2172
2177
|
// so put them at the head of the list
|
|
2173
2178
|
// The list isn't ordered, but we keep the first move at the head
|
package/src/mergeTreeNodes.ts
CHANGED
|
@@ -375,7 +375,6 @@ export abstract class BaseSegment implements ISegment {
|
|
|
375
375
|
overwriteInfo<IMoveInfo>(seg, {
|
|
376
376
|
movedSeq: this.movedSeq,
|
|
377
377
|
movedSeqs: [...this.movedSeqs],
|
|
378
|
-
wasMovedOnInsert: this.wasMovedOnInsert,
|
|
379
378
|
movedClientIds: [...this.movedClientIds],
|
|
380
379
|
});
|
|
381
380
|
}
|
|
@@ -441,7 +440,6 @@ export abstract class BaseSegment implements ISegment {
|
|
|
441
440
|
movedSeq: this.movedSeq,
|
|
442
441
|
movedSeqs: [...this.movedSeqs],
|
|
443
442
|
localMovedSeq: this.localMovedSeq,
|
|
444
|
-
wasMovedOnInsert: this.wasMovedOnInsert,
|
|
445
443
|
});
|
|
446
444
|
}
|
|
447
445
|
|
package/src/partialLengths.ts
CHANGED
|
@@ -14,7 +14,12 @@ import {
|
|
|
14
14
|
seqLTE,
|
|
15
15
|
type MergeBlock,
|
|
16
16
|
} from "./mergeTreeNodes.js";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
toRemovalInfo,
|
|
19
|
+
toMoveInfo,
|
|
20
|
+
assertInserted,
|
|
21
|
+
wasMovedOnInsert,
|
|
22
|
+
} from "./segmentInfos.js";
|
|
18
23
|
import { SortedSet } from "./sortedSet.js";
|
|
19
24
|
|
|
20
25
|
class PartialSequenceLengthsSet extends SortedSet<PartialSequenceLength> {
|
|
@@ -472,8 +477,7 @@ export class PartialSequenceLengths {
|
|
|
472
477
|
if (child.isLeaf()) {
|
|
473
478
|
// Leaf segment
|
|
474
479
|
const segment = child;
|
|
475
|
-
|
|
476
|
-
if (moveInfo?.wasMovedOnInsert) {
|
|
480
|
+
if (wasMovedOnInsert(segment)) {
|
|
477
481
|
PartialSequenceLengths.accountForMoveOnInsert(
|
|
478
482
|
combinedPartialLengths,
|
|
479
483
|
segment,
|
|
@@ -511,7 +515,10 @@ export class PartialSequenceLengths {
|
|
|
511
515
|
): void {
|
|
512
516
|
assertInserted(segment);
|
|
513
517
|
const moveInfo = toMoveInfo(segment);
|
|
514
|
-
assert(
|
|
518
|
+
assert(
|
|
519
|
+
moveInfo !== undefined && wasMovedOnInsert(segment),
|
|
520
|
+
0xab7 /* Segment was not moved on insert */,
|
|
521
|
+
);
|
|
515
522
|
if (moveInfo.movedSeq <= collabWindow.minSeq) {
|
|
516
523
|
// This segment was obliterated as soon as it was inserted, and everyone was aware of the obliterate.
|
|
517
524
|
// Thus every single client treats this segment as length 0 from every perspective, and no adjustments
|
|
@@ -843,7 +850,7 @@ export class PartialSequenceLengths {
|
|
|
843
850
|
segment.seq !== undefined &&
|
|
844
851
|
moveInfo &&
|
|
845
852
|
moveInfo.movedSeq < segment.seq &&
|
|
846
|
-
|
|
853
|
+
wasMovedOnInsert(segment)
|
|
847
854
|
) {
|
|
848
855
|
this.addClientAdjustment(clientId, moveInfo.movedSeq, segment.cachedLength);
|
|
849
856
|
failIncrementalPropagation = true;
|
package/src/segmentInfos.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { assert, isObject } from "@fluidframework/core-utils/internal";
|
|
7
7
|
|
|
8
|
+
import { UnassignedSequenceNumber } from "./constants.js";
|
|
8
9
|
import { ISegmentInternal, ISegmentPrivate, MergeBlock } from "./mergeTreeNodes.js";
|
|
9
10
|
import type { ReferencePosition } from "./referencePositions.js";
|
|
10
11
|
|
|
@@ -296,31 +297,12 @@ export interface IMoveInfo {
|
|
|
296
297
|
* list have all issued concurrent ops to move the segment.
|
|
297
298
|
*/
|
|
298
299
|
movedClientIds: number[];
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* If this segment was inserted into a concurrently moved range and
|
|
302
|
-
* the move op was sequenced before the insertion op. In this case,
|
|
303
|
-
* the segment is visible only to the inserting client
|
|
304
|
-
*
|
|
305
|
-
* `wasMovedOnInsert` only applies for acked obliterates. That is, if
|
|
306
|
-
* a segment inserted by a remote client is moved on insertion by a local
|
|
307
|
-
* and unacked obliterate, we do not consider it as having been moved
|
|
308
|
-
* on insert
|
|
309
|
-
*
|
|
310
|
-
* If a segment is moved on insertion, its length is only ever visible to
|
|
311
|
-
* the client that inserted the segment. This is relevant in partial length
|
|
312
|
-
* calculations
|
|
313
|
-
*
|
|
314
|
-
* @privateRemarks
|
|
315
|
-
* TODO:AB#29553: This property is not persisted in the summary, but it should be.
|
|
316
|
-
*/
|
|
317
|
-
wasMovedOnInsert: boolean;
|
|
318
300
|
}
|
|
301
|
+
|
|
319
302
|
export const toMoveInfo = (segmentLike: unknown): IMoveInfo | undefined =>
|
|
320
303
|
hasProp(segmentLike, "movedClientIds", "array") &&
|
|
321
304
|
hasProp(segmentLike, "movedSeq", "number") &&
|
|
322
|
-
hasProp(segmentLike, "movedSeqs", "array")
|
|
323
|
-
hasProp(segmentLike, "wasMovedOnInsert", "boolean")
|
|
305
|
+
hasProp(segmentLike, "movedSeqs", "array")
|
|
324
306
|
? segmentLike
|
|
325
307
|
: undefined;
|
|
326
308
|
|
|
@@ -334,6 +316,26 @@ export const toMoveInfo = (segmentLike: unknown): IMoveInfo | undefined =>
|
|
|
334
316
|
export const isMoved = (segmentLike: unknown): segmentLike is IMoveInfo =>
|
|
335
317
|
toMoveInfo(segmentLike) !== undefined;
|
|
336
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Returns whether this segment was marked moved as soon as its insertion was acked.
|
|
321
|
+
*
|
|
322
|
+
* This can happen when an an insert occurs concurrent to an obliterate over the range the segment was inserted into,
|
|
323
|
+
* and the obliterate was sequenced first.
|
|
324
|
+
*
|
|
325
|
+
* When this happens, the segment is only ever visible to the client that inserted the segment
|
|
326
|
+
* (and only until that client has seen the obliterate which removed their segment).
|
|
327
|
+
*/
|
|
328
|
+
export function wasMovedOnInsert(segment: IInsertionInfo & ISegmentPrivate): boolean {
|
|
329
|
+
const moveInfo = toMoveInfo(segment);
|
|
330
|
+
const movedSeq = moveInfo?.movedSeq;
|
|
331
|
+
if (movedSeq === undefined || movedSeq === UnassignedSequenceNumber) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const insertSeq = segment.seq;
|
|
336
|
+
return insertSeq === UnassignedSequenceNumber || insertSeq > movedSeq;
|
|
337
|
+
}
|
|
338
|
+
|
|
337
339
|
/**
|
|
338
340
|
* Asserts that the segment has move info. Usage of this function should not produce a user facing error.
|
|
339
341
|
*
|
package/src/snapshotLoader.ts
CHANGED
|
@@ -140,8 +140,6 @@ export class SnapshotLoader {
|
|
|
140
140
|
movedClientIds: spec.movedClientIds.map((id) =>
|
|
141
141
|
this.client.getOrAddShortClientId(id),
|
|
142
142
|
),
|
|
143
|
-
// TODO:AB#29553: This property should be derived from segment data, not hard-coded.
|
|
144
|
-
wasMovedOnInsert: false,
|
|
145
143
|
});
|
|
146
144
|
}
|
|
147
145
|
|