@fluidframework/merge-tree 2.0.0-dev.3.1.0.125672 → 2.0.0-dev.4.2.0.153917
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 +5 -0
- package/README.md +20 -0
- package/dist/attributionCollection.d.ts +76 -13
- package/dist/attributionCollection.d.ts.map +1 -1
- package/dist/attributionCollection.js +173 -54
- package/dist/attributionCollection.js.map +1 -1
- package/dist/attributionPolicy.d.ts +36 -0
- package/dist/attributionPolicy.d.ts.map +1 -0
- package/dist/attributionPolicy.js +165 -0
- package/dist/attributionPolicy.js.map +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +12 -3
- package/dist/client.js.map +1 -1
- package/dist/endOfTreeSegment.d.ts +4 -3
- package/dist/endOfTreeSegment.d.ts.map +1 -1
- package/dist/endOfTreeSegment.js +2 -12
- package/dist/endOfTreeSegment.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/mergeTree.d.ts +64 -9
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +127 -67
- package/dist/mergeTree.js.map +1 -1
- package/dist/mergeTreeTracking.d.ts +24 -4
- package/dist/mergeTreeTracking.d.ts.map +1 -1
- package/dist/mergeTreeTracking.js +48 -11
- package/dist/mergeTreeTracking.js.map +1 -1
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +14 -26
- package/dist/properties.js.map +1 -1
- package/dist/revertibles.d.ts +23 -9
- package/dist/revertibles.d.ts.map +1 -1
- package/dist/revertibles.js +63 -43
- package/dist/revertibles.js.map +1 -1
- package/dist/segmentPropertiesManager.d.ts +1 -0
- package/dist/segmentPropertiesManager.d.ts.map +1 -1
- package/dist/segmentPropertiesManager.js +4 -0
- package/dist/segmentPropertiesManager.js.map +1 -1
- package/dist/snapshotLoader.d.ts.map +1 -1
- package/dist/snapshotLoader.js +14 -4
- package/dist/snapshotLoader.js.map +1 -1
- package/dist/snapshotV1.d.ts.map +1 -1
- package/dist/snapshotV1.js +14 -2
- package/dist/snapshotV1.js.map +1 -1
- package/dist/snapshotlegacy.d.ts.map +1 -1
- package/dist/snapshotlegacy.js +8 -2
- package/dist/snapshotlegacy.js.map +1 -1
- package/dist/zamboni.js +2 -2
- package/dist/zamboni.js.map +1 -1
- package/lib/attributionCollection.d.ts +76 -13
- package/lib/attributionCollection.d.ts.map +1 -1
- package/lib/attributionCollection.js +172 -54
- package/lib/attributionCollection.js.map +1 -1
- package/lib/attributionPolicy.d.ts +36 -0
- package/lib/attributionPolicy.d.ts.map +1 -0
- package/lib/attributionPolicy.js +159 -0
- package/lib/attributionPolicy.js.map +1 -0
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +12 -3
- package/lib/client.js.map +1 -1
- package/lib/endOfTreeSegment.d.ts +4 -3
- package/lib/endOfTreeSegment.d.ts.map +1 -1
- package/lib/endOfTreeSegment.js +2 -12
- package/lib/endOfTreeSegment.js.map +1 -1
- package/lib/index.d.ts +4 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/mergeTree.d.ts +64 -9
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +126 -67
- package/lib/mergeTree.js.map +1 -1
- package/lib/mergeTreeTracking.d.ts +24 -4
- package/lib/mergeTreeTracking.d.ts.map +1 -1
- package/lib/mergeTreeTracking.js +46 -10
- package/lib/mergeTreeTracking.js.map +1 -1
- package/lib/properties.d.ts.map +1 -1
- package/lib/properties.js +14 -26
- package/lib/properties.js.map +1 -1
- package/lib/revertibles.d.ts +23 -9
- package/lib/revertibles.d.ts.map +1 -1
- package/lib/revertibles.js +64 -44
- package/lib/revertibles.js.map +1 -1
- package/lib/segmentPropertiesManager.d.ts +1 -0
- package/lib/segmentPropertiesManager.d.ts.map +1 -1
- package/lib/segmentPropertiesManager.js +4 -0
- package/lib/segmentPropertiesManager.js.map +1 -1
- package/lib/snapshotLoader.d.ts.map +1 -1
- package/lib/snapshotLoader.js +14 -4
- package/lib/snapshotLoader.js.map +1 -1
- package/lib/snapshotV1.d.ts.map +1 -1
- package/lib/snapshotV1.js +14 -2
- package/lib/snapshotV1.js.map +1 -1
- package/lib/snapshotlegacy.d.ts.map +1 -1
- package/lib/snapshotlegacy.js +8 -2
- package/lib/snapshotlegacy.js.map +1 -1
- package/lib/zamboni.js +2 -2
- package/lib/zamboni.js.map +1 -1
- package/package.json +113 -65
- package/src/attributionCollection.ts +280 -71
- package/src/attributionPolicy.ts +250 -0
- package/src/client.ts +13 -2
- package/src/endOfTreeSegment.ts +3 -16
- package/src/index.ts +15 -3
- package/src/mergeTree.ts +205 -92
- package/src/mergeTreeTracking.ts +70 -17
- package/src/properties.ts +17 -26
- package/src/revertibles.ts +104 -47
- package/src/segmentPropertiesManager.ts +4 -0
- package/src/snapshotLoader.ts +18 -5
- package/src/snapshotV1.ts +23 -2
- package/src/snapshotlegacy.ts +10 -2
- package/src/zamboni.ts +2 -2
package/CHANGELOG.md
ADDED
package/README.md
CHANGED
|
@@ -5,6 +5,26 @@ sequence of collaboratively edited items. MergeTree is used in both SharedSequen
|
|
|
5
5
|
|
|
6
6
|
See [GitHub](https://github.com/microsoft/FluidFramework) for more details on the Fluid Framework and packages within.
|
|
7
7
|
|
|
8
|
+
<!-- AUTO-GENERATED-CONTENT:START (README_DEPENDENCY_GUIDELINES_SECTION:includeHeading=TRUE) -->
|
|
9
|
+
|
|
10
|
+
<!-- prettier-ignore-start -->
|
|
11
|
+
<!-- NOTE: This section is automatically generated using @fluid-tools/markdown-magic. Do not update these generated contents directly. -->
|
|
12
|
+
|
|
13
|
+
## Using Fluid Framework libraries
|
|
14
|
+
|
|
15
|
+
When taking a dependency on a Fluid Framework library, we recommend using a `^` (caret) version range, such as `^1.3.4`.
|
|
16
|
+
While Fluid Framework libraries may use different ranges with interdependencies between other Fluid Framework libraries,
|
|
17
|
+
library consumers should always prefer `^`.
|
|
18
|
+
|
|
19
|
+
Note that when depending on a library version of the form 2.0.0-internal.x.y.z, called the Fluid internal version
|
|
20
|
+
scheme, you must use a `>= <` dependency range. Standard `^` and `~` ranges will not work as expected. See the
|
|
21
|
+
[@fluid-tools/version-tools](https://github.com/microsoft/FluidFramework/blob/main/build-tools/packages/version-tools/README.md)
|
|
22
|
+
package for more information including tools to convert between version schemes.
|
|
23
|
+
|
|
24
|
+
<!-- prettier-ignore-end -->
|
|
25
|
+
|
|
26
|
+
<!-- AUTO-GENERATED-CONTENT:END -->
|
|
27
|
+
|
|
8
28
|
## Operations
|
|
9
29
|
|
|
10
30
|
The three basic operations provided by MergeTree are:
|
|
@@ -4,29 +4,79 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { AttributionKey } from "@fluidframework/runtime-definitions";
|
|
6
6
|
import { ISegment } from "./mergeTreeNodes";
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export interface SequenceOffsets {
|
|
8
11
|
/**
|
|
9
12
|
* Parallel array with posBreakpoints which tracks the seq of insertion.
|
|
10
13
|
* Ex: if seqs is [45, 46] and posBreakpoints is [0, 3], the section of the string
|
|
11
14
|
* between offsets 0 and 3 was inserted at seq 45 and the section of the string between
|
|
12
15
|
* 3 and the length of the string was inserted at seq 46.
|
|
16
|
+
*
|
|
17
|
+
* @remarks - We use null here rather than undefined as round-tripping through JSON converts
|
|
18
|
+
* undefineds to null anyway
|
|
13
19
|
*/
|
|
14
|
-
seqs: number[];
|
|
20
|
+
seqs: (number | AttributionKey | null)[];
|
|
15
21
|
posBreakpoints: number[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export interface SerializedAttributionCollection extends SequenceOffsets {
|
|
27
|
+
channels?: {
|
|
28
|
+
[name: string]: SequenceOffsets;
|
|
29
|
+
};
|
|
16
30
|
length: number;
|
|
17
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
export interface IAttributionCollectionSpec<T> {
|
|
36
|
+
root: Iterable<{
|
|
37
|
+
offset: number;
|
|
38
|
+
key: T | null;
|
|
39
|
+
}>;
|
|
40
|
+
channels?: {
|
|
41
|
+
[name: string]: Iterable<{
|
|
42
|
+
offset: number;
|
|
43
|
+
key: T | null;
|
|
44
|
+
}>;
|
|
45
|
+
};
|
|
46
|
+
length: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* @internal
|
|
50
|
+
* @sealed
|
|
51
|
+
*/
|
|
52
|
+
export interface IAttributionCollectionSerializer {
|
|
53
|
+
/**
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
serializeAttributionCollections(segments: Iterable<{
|
|
57
|
+
attribution?: IAttributionCollection<AttributionKey>;
|
|
58
|
+
cachedLength: number;
|
|
59
|
+
}>): SerializedAttributionCollection;
|
|
60
|
+
/**
|
|
61
|
+
* Populates attribution information on segments using the provided summary.
|
|
62
|
+
* @internal
|
|
63
|
+
*/
|
|
64
|
+
populateAttributionCollections(segments: Iterable<ISegment>, summary: SerializedAttributionCollection): void;
|
|
65
|
+
}
|
|
18
66
|
/**
|
|
19
67
|
* @alpha
|
|
20
68
|
*/
|
|
21
69
|
export interface IAttributionCollection<T> {
|
|
22
70
|
/**
|
|
23
71
|
* Retrieves the attribution key associated with the provided offset.
|
|
72
|
+
* @param channel - When specified, gets an attribution key associated with a particular channel.
|
|
24
73
|
*/
|
|
25
|
-
getAtOffset(offset: number):
|
|
74
|
+
getAtOffset(offset: number, channel?: string): AttributionKey | undefined;
|
|
26
75
|
/**
|
|
27
76
|
* Total length of all attribution keys in this collection.
|
|
28
77
|
*/
|
|
29
78
|
readonly length: number;
|
|
79
|
+
readonly channelNames: Iterable<string>;
|
|
30
80
|
/**
|
|
31
81
|
* Retrieve all key/offset pairs stored on this segment. Entries should be ordered by offset, such that
|
|
32
82
|
* the `i`th result's attribution key applies to offsets in the open range between the `i`th offset and the
|
|
@@ -34,41 +84,54 @@ export interface IAttributionCollection<T> {
|
|
|
34
84
|
* The last entry's key applies to the open interval from the last entry's offset to this collection's length.
|
|
35
85
|
* @internal
|
|
36
86
|
*/
|
|
37
|
-
getAll():
|
|
38
|
-
offset: number;
|
|
39
|
-
key: T;
|
|
40
|
-
}>;
|
|
87
|
+
getAll(): IAttributionCollectionSpec<T>;
|
|
41
88
|
/** @internal */
|
|
42
89
|
splitAt(pos: number): IAttributionCollection<T>;
|
|
43
90
|
/** @internal */
|
|
44
91
|
append(other: IAttributionCollection<T>): void;
|
|
45
92
|
/** @internal */
|
|
46
93
|
clone(): IAttributionCollection<T>;
|
|
94
|
+
/**
|
|
95
|
+
* Updates this collection with new attribution data.
|
|
96
|
+
* @param name - Name of the channel that requires an update. Undefined signifies the root channel.
|
|
97
|
+
* Updates apply only to the individual channel (i.e. if an attribution policy needs to update the root
|
|
98
|
+
* channel and 4 other channels, it should call `.update` 5 times).
|
|
99
|
+
* @param channel - Updated collection for that channel.
|
|
100
|
+
* @internal
|
|
101
|
+
*/
|
|
102
|
+
update(name: string | undefined, channel: IAttributionCollection<T>): any;
|
|
47
103
|
}
|
|
104
|
+
export declare function areEqualAttributionKeys(a: AttributionKey | null | undefined, b: AttributionKey | null | undefined): boolean;
|
|
48
105
|
export declare class AttributionCollection implements IAttributionCollection<AttributionKey> {
|
|
49
106
|
private _length;
|
|
50
107
|
private offsets;
|
|
51
108
|
private keys;
|
|
52
|
-
|
|
109
|
+
private channels?;
|
|
110
|
+
private get channelEntries();
|
|
111
|
+
constructor(_length: number, baseEntry?: AttributionKey | null);
|
|
112
|
+
get channelNames(): string[];
|
|
53
113
|
getAtOffset(offset: number): AttributionKey;
|
|
114
|
+
getAtOffset(offset: number, channel: string): AttributionKey | undefined;
|
|
54
115
|
private findIndex;
|
|
116
|
+
private get;
|
|
55
117
|
get length(): number;
|
|
56
118
|
/**
|
|
57
119
|
* Splits this attribution collection into two with entries for [0, pos) and [pos, length).
|
|
58
120
|
*/
|
|
59
121
|
splitAt(pos: number): AttributionCollection;
|
|
60
122
|
append(other: AttributionCollection): void;
|
|
61
|
-
getAll():
|
|
62
|
-
offset: number;
|
|
63
|
-
key: AttributionKey;
|
|
64
|
-
}[];
|
|
123
|
+
getAll(): IAttributionCollectionSpec<AttributionKey>;
|
|
65
124
|
clone(): AttributionCollection;
|
|
125
|
+
update(name: string | undefined, channel: AttributionCollection): void;
|
|
66
126
|
/**
|
|
67
127
|
* Rehydrates attribution information from its serialized form into the provided iterable of consecutive segments.
|
|
68
128
|
*/
|
|
69
|
-
static populateAttributionCollections(segments:
|
|
129
|
+
static populateAttributionCollections(segments: ISegment[], summary: SerializedAttributionCollection): void;
|
|
70
130
|
/**
|
|
71
131
|
* Condenses attribution information on consecutive segments into a `SerializedAttributionCollection`
|
|
132
|
+
*
|
|
133
|
+
* Note: this operates on segments rather than attribution collections directly so that it can handle cases
|
|
134
|
+
* where only some segments have attribution defined.
|
|
72
135
|
*/
|
|
73
136
|
static serializeAttributionCollections(segments: Iterable<{
|
|
74
137
|
attribution?: IAttributionCollection<AttributionKey>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributionCollection.d.ts","sourceRoot":"","sources":["../src/attributionCollection.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,
|
|
1
|
+
{"version":3,"file":"attributionCollection.d.ts","sourceRoot":"","sources":["../src/attributionCollection.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,28 +4,58 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.AttributionCollection = void 0;
|
|
7
|
+
exports.AttributionCollection = exports.areEqualAttributionKeys = void 0;
|
|
8
8
|
const common_utils_1 = require("@fluidframework/common-utils");
|
|
9
|
+
// note: treats null and undefined as equivalent
|
|
9
10
|
function areEqualAttributionKeys(a, b) {
|
|
11
|
+
if (!a && !b) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
if (!a || !b) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
10
17
|
if (a.type !== b.type) {
|
|
11
18
|
return false;
|
|
12
19
|
}
|
|
20
|
+
// Note: TS can't narrow the type of b inside this switch statement, hence the need for casting.
|
|
13
21
|
switch (a.type) {
|
|
14
22
|
case "op":
|
|
15
23
|
return a.seq === b.seq;
|
|
24
|
+
case "detached":
|
|
25
|
+
return a.id === b.id;
|
|
26
|
+
case "local":
|
|
27
|
+
return true;
|
|
16
28
|
default:
|
|
17
|
-
(0, common_utils_1.unreachableCase)(a
|
|
29
|
+
(0, common_utils_1.unreachableCase)(a, "Unhandled AttributionKey type");
|
|
18
30
|
}
|
|
19
31
|
}
|
|
32
|
+
exports.areEqualAttributionKeys = areEqualAttributionKeys;
|
|
20
33
|
class AttributionCollection {
|
|
21
|
-
constructor(
|
|
34
|
+
constructor(_length, baseEntry) {
|
|
22
35
|
this._length = _length;
|
|
23
|
-
this.offsets = [
|
|
24
|
-
this.keys = [
|
|
36
|
+
this.offsets = [];
|
|
37
|
+
this.keys = [];
|
|
38
|
+
if (baseEntry !== undefined) {
|
|
39
|
+
this.offsets.push(0);
|
|
40
|
+
this.keys.push(baseEntry);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
get channelEntries() {
|
|
44
|
+
var _a;
|
|
45
|
+
return Object.entries((_a = this.channels) !== null && _a !== void 0 ? _a : {});
|
|
25
46
|
}
|
|
26
|
-
|
|
47
|
+
get channelNames() {
|
|
48
|
+
var _a;
|
|
49
|
+
return Object.keys((_a = this.channels) !== null && _a !== void 0 ? _a : {});
|
|
50
|
+
}
|
|
51
|
+
getAtOffset(offset, channel) {
|
|
52
|
+
var _a;
|
|
53
|
+
if (channel !== undefined) {
|
|
54
|
+
const subCollection = (_a = this.channels) === null || _a === void 0 ? void 0 : _a[channel];
|
|
55
|
+
return subCollection === null || subCollection === void 0 ? void 0 : subCollection.getAtOffset(offset);
|
|
56
|
+
}
|
|
27
57
|
(0, common_utils_1.assert)(offset >= 0 && offset < this._length, 0x443 /* Requested offset should be valid */);
|
|
28
|
-
return this.
|
|
58
|
+
return this.get(this.findIndex(offset));
|
|
29
59
|
}
|
|
30
60
|
findIndex(offset) {
|
|
31
61
|
// Note: maximum length here is 256 for text segments. Perf testing shows that linear scan beats binary search
|
|
@@ -37,6 +67,10 @@ class AttributionCollection {
|
|
|
37
67
|
}
|
|
38
68
|
return this.offsets[i] === offset ? i : i - 1;
|
|
39
69
|
}
|
|
70
|
+
get(index) {
|
|
71
|
+
const key = this.keys[index];
|
|
72
|
+
return key !== null ? key : undefined;
|
|
73
|
+
}
|
|
40
74
|
get length() {
|
|
41
75
|
return this._length;
|
|
42
76
|
}
|
|
@@ -45,12 +79,17 @@ class AttributionCollection {
|
|
|
45
79
|
*/
|
|
46
80
|
splitAt(pos) {
|
|
47
81
|
const splitIndex = this.findIndex(pos);
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
splitCollection.offsets.push(this.offsets[i] - pos);
|
|
82
|
+
const splitCollection = new AttributionCollection(this.length - pos);
|
|
83
|
+
for (let i = splitIndex; i < this.keys.length; i++) {
|
|
84
|
+
splitCollection.offsets.push(Math.max(this.offsets[i] - pos, 0));
|
|
52
85
|
splitCollection.keys.push(this.keys[i]);
|
|
53
86
|
}
|
|
87
|
+
if (this.channels) {
|
|
88
|
+
splitCollection.channels = {};
|
|
89
|
+
for (const [key, collection] of this.channelEntries) {
|
|
90
|
+
splitCollection.channels[key] = collection.splitAt(pos);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
54
93
|
const spliceIndex = this.offsets[splitIndex] === pos ? splitIndex : splitIndex + 1;
|
|
55
94
|
this.keys.splice(spliceIndex);
|
|
56
95
|
this.offsets.splice(spliceIndex);
|
|
@@ -58,6 +97,8 @@ class AttributionCollection {
|
|
|
58
97
|
return splitCollection;
|
|
59
98
|
}
|
|
60
99
|
append(other) {
|
|
100
|
+
var _a, _b, _c;
|
|
101
|
+
var _d;
|
|
61
102
|
const lastEntry = this.keys[this.keys.length - 1];
|
|
62
103
|
for (let i = 0; i < other.keys.length; i++) {
|
|
63
104
|
if (i !== 0 || !areEqualAttributionKeys(lastEntry, other.keys[i])) {
|
|
@@ -65,82 +106,160 @@ class AttributionCollection {
|
|
|
65
106
|
this.keys.push(other.keys[i]);
|
|
66
107
|
}
|
|
67
108
|
}
|
|
109
|
+
if (other.channels !== undefined || this.channels !== undefined) {
|
|
110
|
+
(_a = this.channels) !== null && _a !== void 0 ? _a : (this.channels = {});
|
|
111
|
+
for (const [key, collection] of other.channelEntries) {
|
|
112
|
+
const thisCollection = ((_b = (_d = this.channels)[key]) !== null && _b !== void 0 ? _b : (_d[key] = new AttributionCollection(this.length, null)));
|
|
113
|
+
thisCollection.append(collection);
|
|
114
|
+
}
|
|
115
|
+
for (const [key, collection] of this.channelEntries) {
|
|
116
|
+
if (((_c = other.channels) === null || _c === void 0 ? void 0 : _c[key]) === undefined) {
|
|
117
|
+
collection.append(new AttributionCollection(other.length, null));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
68
121
|
this._length += other.length;
|
|
69
122
|
}
|
|
70
123
|
getAll() {
|
|
71
|
-
const
|
|
124
|
+
const root = new Array(this.keys.length);
|
|
72
125
|
for (let i = 0; i < this.keys.length; i++) {
|
|
73
|
-
|
|
126
|
+
root[i] = { offset: this.offsets[i], key: this.keys[i] };
|
|
74
127
|
}
|
|
75
|
-
|
|
128
|
+
const result = {
|
|
129
|
+
root,
|
|
130
|
+
length: this.length,
|
|
131
|
+
};
|
|
132
|
+
if (this.channels !== undefined) {
|
|
133
|
+
result.channels = {};
|
|
134
|
+
for (const [key, collection] of this.channelEntries) {
|
|
135
|
+
result.channels[key] = collection.getAll().root;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
76
139
|
}
|
|
77
140
|
clone() {
|
|
78
|
-
const copy = new AttributionCollection(this.
|
|
141
|
+
const copy = new AttributionCollection(this.length);
|
|
79
142
|
copy.keys = this.keys.slice();
|
|
80
143
|
copy.offsets = this.offsets.slice();
|
|
144
|
+
if (this.channels !== undefined) {
|
|
145
|
+
const channelsCopy = {};
|
|
146
|
+
for (const [key, collection] of this.channelEntries) {
|
|
147
|
+
channelsCopy[key] = collection.clone();
|
|
148
|
+
}
|
|
149
|
+
copy.channels = channelsCopy;
|
|
150
|
+
}
|
|
81
151
|
return copy;
|
|
82
152
|
}
|
|
153
|
+
update(name, channel) {
|
|
154
|
+
var _a;
|
|
155
|
+
(0, common_utils_1.assert)(channel.length === this.length, 0x5c0 /* AttributionCollection channel update should have consistent segment length */);
|
|
156
|
+
if (name === undefined) {
|
|
157
|
+
this.offsets = [...channel.offsets];
|
|
158
|
+
this.keys = [...channel.keys];
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
(_a = this.channels) !== null && _a !== void 0 ? _a : (this.channels = {});
|
|
162
|
+
if (this.channels[name] !== undefined) {
|
|
163
|
+
this.channels[name].update(undefined, channel);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
this.channels[name] = channel;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
83
170
|
/**
|
|
84
171
|
* Rehydrates attribution information from its serialized form into the provided iterable of consecutive segments.
|
|
85
172
|
*/
|
|
86
173
|
static populateAttributionCollections(segments, summary) {
|
|
87
|
-
const {
|
|
88
|
-
(0, common_utils_1.assert)(seqs.length === posBreakpoints.length
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
174
|
+
const { channels } = summary;
|
|
175
|
+
(0, common_utils_1.assert)(summary.seqs.length === summary.posBreakpoints.length, 0x445 /* Invalid attribution summary blob provided */);
|
|
176
|
+
const extractOntoSegments = ({ seqs, posBreakpoints }, assignToSegment) => {
|
|
177
|
+
let curIndex = 0;
|
|
178
|
+
let cumulativeSegPos = 0;
|
|
179
|
+
for (const segment of segments) {
|
|
180
|
+
const attribution = new AttributionCollection(segment.cachedLength);
|
|
181
|
+
const pushEntry = (offset, seq) => {
|
|
182
|
+
attribution.offsets.push(offset);
|
|
183
|
+
attribution.keys.push(seq === null ? null : typeof seq === "object" ? seq : { type: "op", seq });
|
|
184
|
+
};
|
|
185
|
+
if (posBreakpoints[curIndex] > cumulativeSegPos) {
|
|
186
|
+
curIndex--;
|
|
187
|
+
}
|
|
188
|
+
while (posBreakpoints[curIndex] < cumulativeSegPos + segment.cachedLength) {
|
|
189
|
+
const nextOffset = Math.max(posBreakpoints[curIndex] - cumulativeSegPos, 0);
|
|
190
|
+
pushEntry(nextOffset, seqs[curIndex]);
|
|
191
|
+
curIndex++;
|
|
100
192
|
}
|
|
101
|
-
|
|
193
|
+
if (attribution.offsets.length === 0) {
|
|
194
|
+
pushEntry(0, seqs[curIndex - 1]);
|
|
195
|
+
}
|
|
196
|
+
assignToSegment(attribution, segment);
|
|
197
|
+
cumulativeSegPos += segment.cachedLength;
|
|
102
198
|
}
|
|
103
|
-
|
|
104
|
-
|
|
199
|
+
};
|
|
200
|
+
extractOntoSegments(summary, (collection, segment) => {
|
|
201
|
+
segment.attribution = collection;
|
|
202
|
+
});
|
|
203
|
+
if (channels) {
|
|
204
|
+
for (const [name, collectionSpec] of Object.entries(channels)) {
|
|
205
|
+
extractOntoSegments(collectionSpec, (collection, segment) => {
|
|
206
|
+
var _a;
|
|
207
|
+
var _b;
|
|
208
|
+
// Cast is valid as we just assigned this field above
|
|
209
|
+
((_a = (_b = segment.attribution).channels) !== null && _a !== void 0 ? _a : (_b.channels = {}))[name] =
|
|
210
|
+
collection;
|
|
211
|
+
});
|
|
105
212
|
}
|
|
106
|
-
segment.attribution = attribution;
|
|
107
|
-
cumulativeSegPos += segment.cachedLength;
|
|
108
213
|
}
|
|
109
214
|
}
|
|
110
215
|
/**
|
|
111
216
|
* Condenses attribution information on consecutive segments into a `SerializedAttributionCollection`
|
|
217
|
+
*
|
|
218
|
+
* Note: this operates on segments rather than attribution collections directly so that it can handle cases
|
|
219
|
+
* where only some segments have attribution defined.
|
|
112
220
|
*/
|
|
113
221
|
static serializeAttributionCollections(segments) {
|
|
114
|
-
var _a
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
let mostRecentAttributionKey;
|
|
118
|
-
let cumulativePos = 0;
|
|
119
|
-
let segmentsWithAttribution = 0;
|
|
120
|
-
let segmentsWithoutAttribution = 0;
|
|
222
|
+
var _a;
|
|
223
|
+
const allCollectionSpecs = [];
|
|
224
|
+
const allChannelNames = new Set();
|
|
121
225
|
for (const segment of segments) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
226
|
+
const collection = (_a = segment.attribution) !== null && _a !== void 0 ? _a : new AttributionCollection(segment.cachedLength, null);
|
|
227
|
+
const spec = collection.getAll();
|
|
228
|
+
allCollectionSpecs.push(spec);
|
|
229
|
+
if (spec.channels) {
|
|
230
|
+
for (const name of Object.keys(spec.channels)) {
|
|
231
|
+
allChannelNames.add(name);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const extractSequenceOffsets = (getSpecEntries) => {
|
|
236
|
+
const posBreakpoints = [];
|
|
237
|
+
const seqs = [];
|
|
238
|
+
let mostRecentAttributionKey;
|
|
239
|
+
let cumulativePos = 0;
|
|
240
|
+
for (const spec of allCollectionSpecs) {
|
|
241
|
+
for (const { offset, key } of getSpecEntries(spec)) {
|
|
242
|
+
(0, common_utils_1.assert)((key === null || key === void 0 ? void 0 : key.type) !== "local", 0x5c1 /* local attribution keys should never be put in summaries */);
|
|
243
|
+
if (mostRecentAttributionKey === undefined ||
|
|
126
244
|
!areEqualAttributionKeys(key, mostRecentAttributionKey)) {
|
|
127
245
|
posBreakpoints.push(offset + cumulativePos);
|
|
128
|
-
seqs.push(key.seq);
|
|
246
|
+
seqs.push(!key ? null : key.type === "op" ? key.seq : key);
|
|
129
247
|
}
|
|
130
248
|
mostRecentAttributionKey = key;
|
|
131
249
|
}
|
|
250
|
+
cumulativePos += spec.length;
|
|
132
251
|
}
|
|
133
|
-
|
|
134
|
-
|
|
252
|
+
return { seqs, posBreakpoints, length: cumulativePos };
|
|
253
|
+
};
|
|
254
|
+
const blobContents = extractSequenceOffsets((spec) => spec.root);
|
|
255
|
+
if (allChannelNames.size > 0) {
|
|
256
|
+
const channels = {};
|
|
257
|
+
for (const name of allChannelNames) {
|
|
258
|
+
const { posBreakpoints, seqs } = extractSequenceOffsets((spec) => { var _a, _b; return (_b = (_a = spec.channels) === null || _a === void 0 ? void 0 : _a[name]) !== null && _b !== void 0 ? _b : [{ offset: 0, key: null }]; });
|
|
259
|
+
channels[name] = { posBreakpoints, seqs };
|
|
135
260
|
}
|
|
136
|
-
|
|
261
|
+
blobContents.channels = channels;
|
|
137
262
|
}
|
|
138
|
-
(0, common_utils_1.assert)(segmentsWithAttribution === 0 || segmentsWithoutAttribution === 0, 0x446 /* Expected either all segments or no segments to have attribution information. */);
|
|
139
|
-
const blobContents = {
|
|
140
|
-
seqs,
|
|
141
|
-
posBreakpoints,
|
|
142
|
-
length: cumulativePos,
|
|
143
|
-
};
|
|
144
263
|
return blobContents;
|
|
145
264
|
}
|
|
146
265
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributionCollection.js","sourceRoot":"","sources":["../src/attributionCollection.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\nimport { assert, unreachableCase } from \"@fluidframework/common-utils\";\nimport { AttributionKey } from \"@fluidframework/runtime-definitions\";\nimport { ISegment } from \"./mergeTreeNodes\";\n\nexport interface SerializedAttributionCollection {\n\t/**\n\t * Parallel array with posBreakpoints which tracks the seq of insertion.\n\t * Ex: if seqs is [45, 46] and posBreakpoints is [0, 3], the section of the string\n\t * between offsets 0 and 3 was inserted at seq 45 and the section of the string between\n\t * 3 and the length of the string was inserted at seq 46.\n\t */\n\tseqs: number[];\n\tposBreakpoints: number[];\n\t/* Total length; only necessary for validation */\n\tlength: number;\n}\n\n/**\n * @alpha\n */\nexport interface IAttributionCollection<T> {\n\t/**\n\t * Retrieves the attribution key associated with the provided offset.\n\t */\n\tgetAtOffset(offset: number): T;\n\n\t/**\n\t * Total length of all attribution keys in this collection.\n\t */\n\treadonly length: number;\n\n\t/**\n\t * Retrieve all key/offset pairs stored on this segment. Entries should be ordered by offset, such that\n\t * the `i`th result's attribution key applies to offsets in the open range between the `i`th offset and the\n\t * `i+1`th offset.\n\t * The last entry's key applies to the open interval from the last entry's offset to this collection's length.\n\t * @internal\n\t */\n\tgetAll(): Iterable<{ offset: number; key: T }>;\n\n\t/** @internal */\n\tsplitAt(pos: number): IAttributionCollection<T>;\n\n\t/** @internal */\n\tappend(other: IAttributionCollection<T>): void;\n\n\t/** @internal */\n\tclone(): IAttributionCollection<T>;\n}\n\nfunction areEqualAttributionKeys(a: AttributionKey, b: AttributionKey): boolean {\n\tif (a.type !== b.type) {\n\t\treturn false;\n\t}\n\n\tswitch (a.type) {\n\t\tcase \"op\":\n\t\t\treturn a.seq === b.seq;\n\t\tdefault:\n\t\t\tunreachableCase(a.type, \"Unhandled AttributionKey type\");\n\t}\n}\n\nexport class AttributionCollection implements IAttributionCollection<AttributionKey> {\n\tprivate offsets: number[];\n\tprivate keys: AttributionKey[];\n\n\tpublic constructor(baseEntry: AttributionKey, private _length: number) {\n\t\tthis.offsets = [0];\n\t\tthis.keys = [baseEntry];\n\t}\n\n\tpublic getAtOffset(offset: number): AttributionKey {\n\t\tassert(offset >= 0 && offset < this._length, 0x443 /* Requested offset should be valid */);\n\t\treturn this.keys[this.findIndex(offset)];\n\t}\n\n\tprivate findIndex(offset: number): number {\n\t\t// Note: maximum length here is 256 for text segments. Perf testing shows that linear scan beats binary search\n\t\t// for attribution collections with under ~64 entries, and even at maximum size (which would require a maximum\n\t\t// length segment with every offset having different attribution), getAtOffset is on the order of 100ns.\n\t\tlet i = 0;\n\t\twhile (i < this.offsets.length && offset > this.offsets[i]) {\n\t\t\ti++;\n\t\t}\n\t\treturn this.offsets[i] === offset ? i : i - 1;\n\t}\n\n\tpublic get length(): number {\n\t\treturn this._length;\n\t}\n\n\t/**\n\t * Splits this attribution collection into two with entries for [0, pos) and [pos, length).\n\t */\n\tpublic splitAt(pos: number): AttributionCollection {\n\t\tconst splitIndex = this.findIndex(pos);\n\t\tconst splitBaseEntry = this.keys[splitIndex];\n\t\tconst splitCollection = new AttributionCollection(splitBaseEntry, this.length - pos);\n\t\tfor (let i = splitIndex + 1; i < this.keys.length; i++) {\n\t\t\tsplitCollection.offsets.push(this.offsets[i] - pos);\n\t\t\tsplitCollection.keys.push(this.keys[i]);\n\t\t}\n\n\t\tconst spliceIndex = this.offsets[splitIndex] === pos ? splitIndex : splitIndex + 1;\n\t\tthis.keys.splice(spliceIndex);\n\t\tthis.offsets.splice(spliceIndex);\n\t\tthis._length = pos;\n\t\treturn splitCollection;\n\t}\n\n\tpublic append(other: AttributionCollection): void {\n\t\tconst lastEntry = this.keys[this.keys.length - 1];\n\t\tfor (let i = 0; i < other.keys.length; i++) {\n\t\t\tif (i !== 0 || !areEqualAttributionKeys(lastEntry, other.keys[i])) {\n\t\t\t\tthis.offsets.push(other.offsets[i] + this.length);\n\t\t\t\tthis.keys.push(other.keys[i]);\n\t\t\t}\n\t\t}\n\t\tthis._length += other.length;\n\t}\n\n\tpublic getAll(): { offset: number; key: AttributionKey }[] {\n\t\tconst results: { offset: number; key: AttributionKey }[] = new Array(this.keys.length);\n\t\tfor (let i = 0; i < this.keys.length; i++) {\n\t\t\tresults[i] = { offset: this.offsets[i], key: this.keys[i] };\n\t\t}\n\t\treturn results;\n\t}\n\n\tpublic clone(): AttributionCollection {\n\t\tconst copy = new AttributionCollection(this.keys[0], this.length);\n\t\tcopy.keys = this.keys.slice();\n\t\tcopy.offsets = this.offsets.slice();\n\t\treturn copy;\n\t}\n\n\t/**\n\t * Rehydrates attribution information from its serialized form into the provided iterable of consecutive segments.\n\t */\n\tpublic static populateAttributionCollections(\n\t\tsegments: Iterable<ISegment>,\n\t\tsummary: SerializedAttributionCollection,\n\t): void {\n\t\tconst { seqs, posBreakpoints } = summary;\n\t\tassert(\n\t\t\tseqs.length === posBreakpoints.length && seqs.length > 0,\n\t\t\t0x445 /* Invalid attribution summary blob provided */,\n\t\t);\n\t\tlet curIndex = 0;\n\t\tlet currentInfo = seqs[curIndex];\n\t\tlet cumulativeSegPos = 0;\n\n\t\tfor (const segment of segments) {\n\t\t\tconst attribution = new AttributionCollection(\n\t\t\t\t{ type: \"op\", seq: currentInfo },\n\t\t\t\tsegment.cachedLength,\n\t\t\t);\n\t\t\twhile (posBreakpoints[curIndex] < cumulativeSegPos + segment.cachedLength) {\n\t\t\t\tcurrentInfo = seqs[curIndex];\n\t\t\t\tconst nextOffset = posBreakpoints[curIndex] - cumulativeSegPos;\n\t\t\t\tif (attribution.offsets[attribution.offsets.length - 1] !== nextOffset) {\n\t\t\t\t\tattribution.offsets.push(nextOffset);\n\t\t\t\t\tattribution.keys.push({ type: \"op\", seq: currentInfo });\n\t\t\t\t}\n\t\t\t\tcurIndex++;\n\t\t\t}\n\n\t\t\tif (posBreakpoints[curIndex] === cumulativeSegPos + segment.cachedLength) {\n\t\t\t\tcurrentInfo = seqs[curIndex];\n\t\t\t}\n\n\t\t\tsegment.attribution = attribution;\n\t\t\tcumulativeSegPos += segment.cachedLength;\n\t\t}\n\t}\n\n\t/**\n\t * Condenses attribution information on consecutive segments into a `SerializedAttributionCollection`\n\t */\n\tpublic static serializeAttributionCollections(\n\t\tsegments: Iterable<{\n\t\t\tattribution?: IAttributionCollection<AttributionKey>;\n\t\t\tcachedLength: number;\n\t\t}>,\n\t): SerializedAttributionCollection {\n\t\tconst posBreakpoints: number[] = [];\n\t\tconst seqs: number[] = [];\n\t\tlet mostRecentAttributionKey: AttributionKey | undefined;\n\t\tlet cumulativePos = 0;\n\n\t\tlet segmentsWithAttribution = 0;\n\t\tlet segmentsWithoutAttribution = 0;\n\t\tfor (const segment of segments) {\n\t\t\tif (segment.attribution) {\n\t\t\t\tsegmentsWithAttribution++;\n\t\t\t\tfor (const { offset, key } of segment.attribution?.getAll() ?? []) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!mostRecentAttributionKey ||\n\t\t\t\t\t\t!areEqualAttributionKeys(key, mostRecentAttributionKey)\n\t\t\t\t\t) {\n\t\t\t\t\t\tposBreakpoints.push(offset + cumulativePos);\n\t\t\t\t\t\tseqs.push(key.seq);\n\t\t\t\t\t}\n\t\t\t\t\tmostRecentAttributionKey = key;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsegmentsWithoutAttribution++;\n\t\t\t}\n\n\t\t\tcumulativePos += segment.cachedLength;\n\t\t}\n\n\t\tassert(\n\t\t\tsegmentsWithAttribution === 0 || segmentsWithoutAttribution === 0,\n\t\t\t0x446 /* Expected either all segments or no segments to have attribution information. */,\n\t\t);\n\n\t\tconst blobContents: SerializedAttributionCollection = {\n\t\t\tseqs,\n\t\t\tposBreakpoints,\n\t\t\tlength: cumulativePos,\n\t\t};\n\t\treturn blobContents;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"attributionCollection.js","sourceRoot":"","sources":["../src/attributionCollection.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\nimport { assert, unreachableCase } from \"@fluidframework/common-utils\";\nimport {\n\tAttributionKey,\n\tOpAttributionKey,\n\tDetachedAttributionKey,\n} from \"@fluidframework/runtime-definitions\";\nimport { ISegment } from \"./mergeTreeNodes\";\n\n/**\n * @internal\n */\nexport interface SequenceOffsets {\n\t/**\n\t * Parallel array with posBreakpoints which tracks the seq of insertion.\n\t * Ex: if seqs is [45, 46] and posBreakpoints is [0, 3], the section of the string\n\t * between offsets 0 and 3 was inserted at seq 45 and the section of the string between\n\t * 3 and the length of the string was inserted at seq 46.\n\t *\n\t * @remarks - We use null here rather than undefined as round-tripping through JSON converts\n\t * undefineds to null anyway\n\t */\n\tseqs: (number | AttributionKey | null)[];\n\tposBreakpoints: number[];\n}\n\n/**\n * @internal\n */\nexport interface SerializedAttributionCollection extends SequenceOffsets {\n\tchannels?: { [name: string]: SequenceOffsets };\n\t/* Total length; only necessary for validation */\n\tlength: number;\n}\n\n/**\n * @internal\n */\nexport interface IAttributionCollectionSpec<T> {\n\troot: Iterable<{ offset: number; key: T | null }>;\n\tchannels?: { [name: string]: Iterable<{ offset: number; key: T | null }> };\n\tlength: number;\n}\n\n/**\n * @internal\n * @sealed\n */\nexport interface IAttributionCollectionSerializer {\n\t/**\n\t * @internal\n\t */\n\tserializeAttributionCollections(\n\t\tsegments: Iterable<{\n\t\t\tattribution?: IAttributionCollection<AttributionKey>;\n\t\t\tcachedLength: number;\n\t\t}>,\n\t): SerializedAttributionCollection;\n\n\t/**\n\t * Populates attribution information on segments using the provided summary.\n\t * @internal\n\t */\n\tpopulateAttributionCollections(\n\t\tsegments: Iterable<ISegment>,\n\t\tsummary: SerializedAttributionCollection,\n\t): void;\n}\n\n/**\n * @alpha\n */\nexport interface IAttributionCollection<T> {\n\t/**\n\t * Retrieves the attribution key associated with the provided offset.\n\t * @param channel - When specified, gets an attribution key associated with a particular channel.\n\t */\n\tgetAtOffset(offset: number, channel?: string): AttributionKey | undefined;\n\n\t/**\n\t * Total length of all attribution keys in this collection.\n\t */\n\treadonly length: number;\n\n\treadonly channelNames: Iterable<string>;\n\n\t/**\n\t * Retrieve all key/offset pairs stored on this segment. Entries should be ordered by offset, such that\n\t * the `i`th result's attribution key applies to offsets in the open range between the `i`th offset and the\n\t * `i+1`th offset.\n\t * The last entry's key applies to the open interval from the last entry's offset to this collection's length.\n\t * @internal\n\t */\n\tgetAll(): IAttributionCollectionSpec<T>;\n\n\t/** @internal */\n\tsplitAt(pos: number): IAttributionCollection<T>;\n\n\t/** @internal */\n\tappend(other: IAttributionCollection<T>): void;\n\n\t/** @internal */\n\tclone(): IAttributionCollection<T>;\n\n\t/**\n\t * Updates this collection with new attribution data.\n\t * @param name - Name of the channel that requires an update. Undefined signifies the root channel.\n\t * Updates apply only to the individual channel (i.e. if an attribution policy needs to update the root\n\t * channel and 4 other channels, it should call `.update` 5 times).\n\t * @param channel - Updated collection for that channel.\n\t * @internal\n\t */\n\tupdate(name: string | undefined, channel: IAttributionCollection<T>);\n}\n\n// note: treats null and undefined as equivalent\nexport function areEqualAttributionKeys(\n\ta: AttributionKey | null | undefined,\n\tb: AttributionKey | null | undefined,\n): boolean {\n\tif (!a && !b) {\n\t\treturn true;\n\t}\n\n\tif (!a || !b) {\n\t\treturn false;\n\t}\n\n\tif (a.type !== b.type) {\n\t\treturn false;\n\t}\n\n\t// Note: TS can't narrow the type of b inside this switch statement, hence the need for casting.\n\tswitch (a.type) {\n\t\tcase \"op\":\n\t\t\treturn a.seq === (b as OpAttributionKey).seq;\n\t\tcase \"detached\":\n\t\t\treturn a.id === (b as DetachedAttributionKey).id;\n\t\tcase \"local\":\n\t\t\treturn true;\n\t\tdefault:\n\t\t\tunreachableCase(a, \"Unhandled AttributionKey type\");\n\t}\n}\n\nexport class AttributionCollection implements IAttributionCollection<AttributionKey> {\n\tprivate offsets: number[] = [];\n\tprivate keys: (AttributionKey | null)[] = [];\n\n\tprivate channels?: { [name: string]: AttributionCollection };\n\n\tprivate get channelEntries(): [string, AttributionCollection][] {\n\t\treturn Object.entries(this.channels ?? {});\n\t}\n\n\tpublic constructor(private _length: number, baseEntry?: AttributionKey | null) {\n\t\tif (baseEntry !== undefined) {\n\t\t\tthis.offsets.push(0);\n\t\t\tthis.keys.push(baseEntry);\n\t\t}\n\t}\n\n\tpublic get channelNames(): string[] {\n\t\treturn Object.keys(this.channels ?? {});\n\t}\n\n\tpublic getAtOffset(offset: number): AttributionKey;\n\tpublic getAtOffset(offset: number, channel: string): AttributionKey | undefined;\n\tpublic getAtOffset(offset: number, channel?: string): AttributionKey | undefined {\n\t\tif (channel !== undefined) {\n\t\t\tconst subCollection = this.channels?.[channel];\n\t\t\treturn subCollection?.getAtOffset(offset);\n\t\t}\n\t\tassert(offset >= 0 && offset < this._length, 0x443 /* Requested offset should be valid */);\n\t\treturn this.get(this.findIndex(offset));\n\t}\n\n\tprivate findIndex(offset: number): number {\n\t\t// Note: maximum length here is 256 for text segments. Perf testing shows that linear scan beats binary search\n\t\t// for attribution collections with under ~64 entries, and even at maximum size (which would require a maximum\n\t\t// length segment with every offset having different attribution), getAtOffset is on the order of 100ns.\n\t\tlet i = 0;\n\t\twhile (i < this.offsets.length && offset > this.offsets[i]) {\n\t\t\ti++;\n\t\t}\n\t\treturn this.offsets[i] === offset ? i : i - 1;\n\t}\n\n\tprivate get(index: number): AttributionKey | undefined {\n\t\tconst key = this.keys[index];\n\t\treturn key !== null ? key : undefined;\n\t}\n\n\tpublic get length(): number {\n\t\treturn this._length;\n\t}\n\n\t/**\n\t * Splits this attribution collection into two with entries for [0, pos) and [pos, length).\n\t */\n\tpublic splitAt(pos: number): AttributionCollection {\n\t\tconst splitIndex = this.findIndex(pos);\n\t\tconst splitCollection = new AttributionCollection(this.length - pos);\n\t\tfor (let i = splitIndex; i < this.keys.length; i++) {\n\t\t\tsplitCollection.offsets.push(Math.max(this.offsets[i] - pos, 0));\n\t\t\tsplitCollection.keys.push(this.keys[i]);\n\t\t}\n\n\t\tif (this.channels) {\n\t\t\tsplitCollection.channels = {};\n\t\t\tfor (const [key, collection] of this.channelEntries) {\n\t\t\t\tsplitCollection.channels[key] = collection.splitAt(pos);\n\t\t\t}\n\t\t}\n\n\t\tconst spliceIndex = this.offsets[splitIndex] === pos ? splitIndex : splitIndex + 1;\n\t\tthis.keys.splice(spliceIndex);\n\t\tthis.offsets.splice(spliceIndex);\n\t\tthis._length = pos;\n\t\treturn splitCollection;\n\t}\n\n\tpublic append(other: AttributionCollection): void {\n\t\tconst lastEntry = this.keys[this.keys.length - 1];\n\t\tfor (let i = 0; i < other.keys.length; i++) {\n\t\t\tif (i !== 0 || !areEqualAttributionKeys(lastEntry, other.keys[i])) {\n\t\t\t\tthis.offsets.push(other.offsets[i] + this.length);\n\t\t\t\tthis.keys.push(other.keys[i]);\n\t\t\t}\n\t\t}\n\n\t\tif (other.channels !== undefined || this.channels !== undefined) {\n\t\t\tthis.channels ??= {};\n\t\t\tfor (const [key, collection] of other.channelEntries) {\n\t\t\t\tconst thisCollection = (this.channels[key] ??= new AttributionCollection(\n\t\t\t\t\tthis.length,\n\t\t\t\t\tnull,\n\t\t\t\t));\n\t\t\t\tthisCollection.append(collection);\n\t\t\t}\n\t\t\tfor (const [key, collection] of this.channelEntries) {\n\t\t\t\tif (other.channels?.[key] === undefined) {\n\t\t\t\t\tcollection.append(new AttributionCollection(other.length, null));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis._length += other.length;\n\t}\n\n\tpublic getAll(): IAttributionCollectionSpec<AttributionKey> {\n\t\tconst root: IAttributionCollectionSpec<AttributionKey>[\"root\"] = new Array(\n\t\t\tthis.keys.length,\n\t\t);\n\t\tfor (let i = 0; i < this.keys.length; i++) {\n\t\t\troot[i] = { offset: this.offsets[i], key: this.keys[i] };\n\t\t}\n\t\tconst result: IAttributionCollectionSpec<AttributionKey> = {\n\t\t\troot,\n\t\t\tlength: this.length,\n\t\t};\n\t\tif (this.channels !== undefined) {\n\t\t\tresult.channels = {};\n\t\t\tfor (const [key, collection] of this.channelEntries) {\n\t\t\t\tresult.channels[key] = collection.getAll().root;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic clone(): AttributionCollection {\n\t\tconst copy = new AttributionCollection(this.length);\n\t\tcopy.keys = this.keys.slice();\n\t\tcopy.offsets = this.offsets.slice();\n\t\tif (this.channels !== undefined) {\n\t\t\tconst channelsCopy = {};\n\t\t\tfor (const [key, collection] of this.channelEntries) {\n\t\t\t\tchannelsCopy[key] = collection.clone();\n\t\t\t}\n\t\t\tcopy.channels = channelsCopy;\n\t\t}\n\t\treturn copy;\n\t}\n\n\tpublic update(name: string | undefined, channel: AttributionCollection) {\n\t\tassert(\n\t\t\tchannel.length === this.length,\n\t\t\t0x5c0 /* AttributionCollection channel update should have consistent segment length */,\n\t\t);\n\t\tif (name === undefined) {\n\t\t\tthis.offsets = [...channel.offsets];\n\t\t\tthis.keys = [...channel.keys];\n\t\t} else {\n\t\t\tthis.channels ??= {};\n\t\t\tif (this.channels[name] !== undefined) {\n\t\t\t\tthis.channels[name].update(undefined, channel);\n\t\t\t} else {\n\t\t\t\tthis.channels[name] = channel;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Rehydrates attribution information from its serialized form into the provided iterable of consecutive segments.\n\t */\n\tpublic static populateAttributionCollections(\n\t\tsegments: ISegment[],\n\t\tsummary: SerializedAttributionCollection,\n\t): void {\n\t\tconst { channels } = summary;\n\t\tassert(\n\t\t\tsummary.seqs.length === summary.posBreakpoints.length,\n\t\t\t0x445 /* Invalid attribution summary blob provided */,\n\t\t);\n\n\t\tconst extractOntoSegments = (\n\t\t\t{ seqs, posBreakpoints }: SequenceOffsets,\n\t\t\tassignToSegment: (collection: AttributionCollection, segment: ISegment) => void,\n\t\t) => {\n\t\t\tlet curIndex = 0;\n\t\t\tlet cumulativeSegPos = 0;\n\n\t\t\tfor (const segment of segments) {\n\t\t\t\tconst attribution = new AttributionCollection(segment.cachedLength);\n\t\t\t\tconst pushEntry = (offset: number, seq: AttributionKey | number | null) => {\n\t\t\t\t\tattribution.offsets.push(offset);\n\t\t\t\t\tattribution.keys.push(\n\t\t\t\t\t\tseq === null ? null : typeof seq === \"object\" ? seq : { type: \"op\", seq },\n\t\t\t\t\t);\n\t\t\t\t};\n\t\t\t\tif (posBreakpoints[curIndex] > cumulativeSegPos) {\n\t\t\t\t\tcurIndex--;\n\t\t\t\t}\n\n\t\t\t\twhile (posBreakpoints[curIndex] < cumulativeSegPos + segment.cachedLength) {\n\t\t\t\t\tconst nextOffset = Math.max(posBreakpoints[curIndex] - cumulativeSegPos, 0);\n\t\t\t\t\tpushEntry(nextOffset, seqs[curIndex]);\n\t\t\t\t\tcurIndex++;\n\t\t\t\t}\n\n\t\t\t\tif (attribution.offsets.length === 0) {\n\t\t\t\t\tpushEntry(0, seqs[curIndex - 1]);\n\t\t\t\t}\n\n\t\t\t\tassignToSegment(attribution, segment);\n\t\t\t\tcumulativeSegPos += segment.cachedLength;\n\t\t\t}\n\t\t};\n\n\t\textractOntoSegments(summary, (collection, segment) => {\n\t\t\tsegment.attribution = collection;\n\t\t});\n\t\tif (channels) {\n\t\t\tfor (const [name, collectionSpec] of Object.entries(channels)) {\n\t\t\t\textractOntoSegments(collectionSpec, (collection, segment) => {\n\t\t\t\t\t// Cast is valid as we just assigned this field above\n\t\t\t\t\t((segment.attribution as AttributionCollection).channels ??= {})[name] =\n\t\t\t\t\t\tcollection;\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Condenses attribution information on consecutive segments into a `SerializedAttributionCollection`\n\t *\n\t * Note: this operates on segments rather than attribution collections directly so that it can handle cases\n\t * where only some segments have attribution defined.\n\t */\n\tpublic static serializeAttributionCollections(\n\t\tsegments: Iterable<{\n\t\t\tattribution?: IAttributionCollection<AttributionKey>;\n\t\t\tcachedLength: number;\n\t\t}>,\n\t): SerializedAttributionCollection {\n\t\tconst allCollectionSpecs: IAttributionCollectionSpec<AttributionKey>[] = [];\n\n\t\tconst allChannelNames = new Set<string>();\n\t\tfor (const segment of segments) {\n\t\t\tconst collection =\n\t\t\t\tsegment.attribution ?? new AttributionCollection(segment.cachedLength, null);\n\t\t\tconst spec = collection.getAll();\n\t\t\tallCollectionSpecs.push(spec);\n\t\t\tif (spec.channels) {\n\t\t\t\tfor (const name of Object.keys(spec.channels)) {\n\t\t\t\t\tallChannelNames.add(name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst extractSequenceOffsets = (\n\t\t\tgetSpecEntries: (\n\t\t\t\tspec: IAttributionCollectionSpec<AttributionKey>,\n\t\t\t) => Iterable<{ offset: number; key: AttributionKey | null }>,\n\t\t): SerializedAttributionCollection => {\n\t\t\tconst posBreakpoints: number[] = [];\n\t\t\tconst seqs: (number | AttributionKey | null)[] = [];\n\t\t\tlet mostRecentAttributionKey: AttributionKey | null | undefined;\n\t\t\tlet cumulativePos = 0;\n\n\t\t\tfor (const spec of allCollectionSpecs) {\n\t\t\t\tfor (const { offset, key } of getSpecEntries(spec)) {\n\t\t\t\t\tassert(\n\t\t\t\t\t\tkey?.type !== \"local\",\n\t\t\t\t\t\t0x5c1 /* local attribution keys should never be put in summaries */,\n\t\t\t\t\t);\n\t\t\t\t\tif (\n\t\t\t\t\t\tmostRecentAttributionKey === undefined ||\n\t\t\t\t\t\t!areEqualAttributionKeys(key, mostRecentAttributionKey)\n\t\t\t\t\t) {\n\t\t\t\t\t\tposBreakpoints.push(offset + cumulativePos);\n\t\t\t\t\t\tseqs.push(!key ? null : key.type === \"op\" ? key.seq : key);\n\t\t\t\t\t}\n\t\t\t\t\tmostRecentAttributionKey = key;\n\t\t\t\t}\n\n\t\t\t\tcumulativePos += spec.length;\n\t\t\t}\n\n\t\t\treturn { seqs, posBreakpoints, length: cumulativePos };\n\t\t};\n\n\t\tconst blobContents = extractSequenceOffsets((spec) => spec.root);\n\t\tif (allChannelNames.size > 0) {\n\t\t\tconst channels: { [name: string]: SequenceOffsets } = {};\n\t\t\tfor (const name of allChannelNames) {\n\t\t\t\tconst { posBreakpoints, seqs } = extractSequenceOffsets(\n\t\t\t\t\t(spec) => spec.channels?.[name] ?? [{ offset: 0, key: null }],\n\t\t\t\t);\n\t\t\t\tchannels[name] = { posBreakpoints, seqs };\n\t\t\t}\n\t\t\tblobContents.channels = channels;\n\t\t}\n\n\t\treturn blobContents;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { AttributionPolicy } from "./mergeTree";
|
|
6
|
+
/**
|
|
7
|
+
* @alpha
|
|
8
|
+
* @returns - An {@link AttributionPolicy} which tracks only insertion of content.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createInsertOnlyAttributionPolicy(): AttributionPolicy;
|
|
11
|
+
/**
|
|
12
|
+
* @param propNames - List of property names for which attribution should be tracked.
|
|
13
|
+
* @returns - A policy which only attributes annotation of the properties specified.
|
|
14
|
+
* Keys for each property are stored under attribution channels of the same name--see example below.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
*
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // Use this policy when creating your merge-tree:
|
|
20
|
+
* const policy = createPropertyTrackingAttributionPolicyFactory("bold", "italic");
|
|
21
|
+
* // ... later, you can get attribution keys for the last time the "bold" and "italic"
|
|
22
|
+
* // properties were changed on a segment using `getAtOffset`:
|
|
23
|
+
* const lastBoldedAttributionKey = segment.attribution?.getAtOffset(0, "bold");
|
|
24
|
+
* const lastItalicizedAttributionKey = segment.attribution?.getAtOffset(0, "italic");
|
|
25
|
+
* ```
|
|
26
|
+
* @alpha
|
|
27
|
+
*/
|
|
28
|
+
export declare function createPropertyTrackingAttributionPolicyFactory(...propNames: string[]): () => AttributionPolicy;
|
|
29
|
+
/**
|
|
30
|
+
* Creates an attribution policy which tracks insertion as well as annotation of certain property names.
|
|
31
|
+
* This combines the policies creatable using {@link createPropertyTrackingAttributionPolicyFactory} and
|
|
32
|
+
* {@link createInsertOnlyAttributionPolicy}: see there for more details.
|
|
33
|
+
* @alpha
|
|
34
|
+
*/
|
|
35
|
+
export declare function createPropertyTrackingAndInsertionAttributionPolicyFactory(...propNames: string[]): () => AttributionPolicy;
|
|
36
|
+
//# sourceMappingURL=attributionPolicy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attributionPolicy.d.ts","sourceRoot":"","sources":["../src/attributionPolicy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAsLhD;;;GAGG;AACH,wBAAgB,iCAAiC,IAAI,iBAAiB,CAOrE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,8CAA8C,CAC7D,GAAG,SAAS,EAAE,MAAM,EAAE,GACpB,MAAM,iBAAiB,CAQzB;AAED;;;;;GAKG;AACH,wBAAgB,0DAA0D,CACzE,GAAG,SAAS,EAAE,MAAM,EAAE,GACpB,MAAM,iBAAiB,CASzB"}
|