@fluidframework/merge-tree 2.0.0-internal.4.0.5 → 2.0.0-internal.4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/README.md +20 -0
- package/dist/attributionCollection.d.ts +55 -22
- package/dist/attributionCollection.d.ts.map +1 -1
- package/dist/attributionCollection.js +166 -112
- 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.js +3 -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 +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/mergeTree.d.ts +12 -7
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +86 -18
- 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/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.js.map +1 -1
- package/dist/zamboni.js +2 -2
- package/dist/zamboni.js.map +1 -1
- package/lib/attributionCollection.d.ts +55 -22
- package/lib/attributionCollection.d.ts.map +1 -1
- package/lib/attributionCollection.js +165 -110
- 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.js +3 -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 +3 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/mergeTree.d.ts +12 -7
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +84 -17
- 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/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.js.map +1 -1
- package/lib/zamboni.js +2 -2
- package/lib/zamboni.js.map +1 -1
- package/package.json +77 -17
- package/src/attributionCollection.ts +238 -157
- package/src/attributionPolicy.ts +250 -0
- package/src/client.ts +2 -2
- package/src/endOfTreeSegment.ts +3 -16
- package/src/index.ts +9 -2
- package/src/mergeTree.ts +122 -24
- package/src/mergeTreeTracking.ts +70 -17
- package/src/revertibles.ts +104 -47
- package/src/segmentPropertiesManager.ts +4 -0
- package/src/snapshotLoader.ts +1 -1
- 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:
|
|
@@ -3,20 +3,46 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { AttributionKey } from "@fluidframework/runtime-definitions";
|
|
6
|
-
import { AttributionPolicy } from "./mergeTree";
|
|
7
6
|
import { ISegment } from "./mergeTreeNodes";
|
|
8
7
|
/**
|
|
9
8
|
* @internal
|
|
10
9
|
*/
|
|
11
|
-
export interface
|
|
10
|
+
export interface SequenceOffsets {
|
|
12
11
|
/**
|
|
13
12
|
* Parallel array with posBreakpoints which tracks the seq of insertion.
|
|
14
13
|
* Ex: if seqs is [45, 46] and posBreakpoints is [0, 3], the section of the string
|
|
15
14
|
* between offsets 0 and 3 was inserted at seq 45 and the section of the string between
|
|
16
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
|
|
17
19
|
*/
|
|
18
|
-
seqs: (number | AttributionKey)[];
|
|
20
|
+
seqs: (number | AttributionKey | null)[];
|
|
19
21
|
posBreakpoints: number[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export interface SerializedAttributionCollection extends SequenceOffsets {
|
|
27
|
+
channels?: {
|
|
28
|
+
[name: string]: SequenceOffsets;
|
|
29
|
+
};
|
|
30
|
+
length: number;
|
|
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
|
+
};
|
|
20
46
|
length: number;
|
|
21
47
|
}
|
|
22
48
|
/**
|
|
@@ -43,12 +69,14 @@ export interface IAttributionCollectionSerializer {
|
|
|
43
69
|
export interface IAttributionCollection<T> {
|
|
44
70
|
/**
|
|
45
71
|
* Retrieves the attribution key associated with the provided offset.
|
|
72
|
+
* @param channel - When specified, gets an attribution key associated with a particular channel.
|
|
46
73
|
*/
|
|
47
|
-
getAtOffset(offset: number):
|
|
74
|
+
getAtOffset(offset: number, channel?: string): AttributionKey | undefined;
|
|
48
75
|
/**
|
|
49
76
|
* Total length of all attribution keys in this collection.
|
|
50
77
|
*/
|
|
51
78
|
readonly length: number;
|
|
79
|
+
readonly channelNames: Iterable<string>;
|
|
52
80
|
/**
|
|
53
81
|
* Retrieve all key/offset pairs stored on this segment. Entries should be ordered by offset, such that
|
|
54
82
|
* the `i`th result's attribution key applies to offsets in the open range between the `i`th offset and the
|
|
@@ -56,53 +84,58 @@ export interface IAttributionCollection<T> {
|
|
|
56
84
|
* The last entry's key applies to the open interval from the last entry's offset to this collection's length.
|
|
57
85
|
* @internal
|
|
58
86
|
*/
|
|
59
|
-
getAll():
|
|
60
|
-
offset: number;
|
|
61
|
-
key: T;
|
|
62
|
-
}>;
|
|
87
|
+
getAll(): IAttributionCollectionSpec<T>;
|
|
63
88
|
/** @internal */
|
|
64
89
|
splitAt(pos: number): IAttributionCollection<T>;
|
|
65
90
|
/** @internal */
|
|
66
91
|
append(other: IAttributionCollection<T>): void;
|
|
67
92
|
/** @internal */
|
|
68
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;
|
|
69
103
|
}
|
|
70
|
-
export declare function areEqualAttributionKeys(a: AttributionKey, b: AttributionKey): boolean;
|
|
104
|
+
export declare function areEqualAttributionKeys(a: AttributionKey | null | undefined, b: AttributionKey | null | undefined): boolean;
|
|
71
105
|
export declare class AttributionCollection implements IAttributionCollection<AttributionKey> {
|
|
72
106
|
private _length;
|
|
73
107
|
private offsets;
|
|
74
108
|
private keys;
|
|
75
|
-
|
|
109
|
+
private channels?;
|
|
110
|
+
private get channelEntries();
|
|
111
|
+
constructor(_length: number, baseEntry?: AttributionKey | null);
|
|
112
|
+
get channelNames(): string[];
|
|
76
113
|
getAtOffset(offset: number): AttributionKey;
|
|
114
|
+
getAtOffset(offset: number, channel: string): AttributionKey | undefined;
|
|
77
115
|
private findIndex;
|
|
116
|
+
private get;
|
|
78
117
|
get length(): number;
|
|
79
118
|
/**
|
|
80
119
|
* Splits this attribution collection into two with entries for [0, pos) and [pos, length).
|
|
81
120
|
*/
|
|
82
121
|
splitAt(pos: number): AttributionCollection;
|
|
83
122
|
append(other: AttributionCollection): void;
|
|
84
|
-
getAll():
|
|
85
|
-
offset: number;
|
|
86
|
-
key: AttributionKey;
|
|
87
|
-
}[];
|
|
123
|
+
getAll(): IAttributionCollectionSpec<AttributionKey>;
|
|
88
124
|
clone(): AttributionCollection;
|
|
125
|
+
update(name: string | undefined, channel: AttributionCollection): void;
|
|
89
126
|
/**
|
|
90
127
|
* Rehydrates attribution information from its serialized form into the provided iterable of consecutive segments.
|
|
91
128
|
*/
|
|
92
|
-
static populateAttributionCollections(segments:
|
|
129
|
+
static populateAttributionCollections(segments: ISegment[], summary: SerializedAttributionCollection): void;
|
|
93
130
|
/**
|
|
94
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.
|
|
95
135
|
*/
|
|
96
136
|
static serializeAttributionCollections(segments: Iterable<{
|
|
97
137
|
attribution?: IAttributionCollection<AttributionKey>;
|
|
98
138
|
cachedLength: number;
|
|
99
139
|
}>): SerializedAttributionCollection;
|
|
100
140
|
}
|
|
101
|
-
/**
|
|
102
|
-
* @alpha
|
|
103
|
-
* @returns - An {@link AttributionPolicy} which tracks only insertion of content.
|
|
104
|
-
* Content is only attributed at ack time, unless the container is in a detached state.
|
|
105
|
-
* Detached content is attributed with a {@link @fluidframework/runtime-definitions#DetachedAttributionKey}.
|
|
106
|
-
*/
|
|
107
|
-
export declare function createInsertOnlyAttributionPolicy(): AttributionPolicy;
|
|
108
141
|
//# sourceMappingURL=attributionCollection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributionCollection.d.ts","sourceRoot":"","sources":["../src/attributionCollection.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACN,cAAc,EAGd,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"attributionCollection.d.ts","sourceRoot":"","sources":["../src/attributionCollection.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,12 +4,16 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.
|
|
7
|
+
exports.AttributionCollection = exports.areEqualAttributionKeys = void 0;
|
|
8
8
|
const common_utils_1 = require("@fluidframework/common-utils");
|
|
9
|
-
|
|
10
|
-
const mergeTreeDeltaCallback_1 = require("./mergeTreeDeltaCallback");
|
|
11
|
-
const ops_1 = require("./ops");
|
|
9
|
+
// note: treats null and undefined as equivalent
|
|
12
10
|
function areEqualAttributionKeys(a, b) {
|
|
11
|
+
if (!a && !b) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
if (!a || !b) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
13
17
|
if (a.type !== b.type) {
|
|
14
18
|
return false;
|
|
15
19
|
}
|
|
@@ -27,14 +31,31 @@ function areEqualAttributionKeys(a, b) {
|
|
|
27
31
|
}
|
|
28
32
|
exports.areEqualAttributionKeys = areEqualAttributionKeys;
|
|
29
33
|
class AttributionCollection {
|
|
30
|
-
constructor(
|
|
34
|
+
constructor(_length, baseEntry) {
|
|
31
35
|
this._length = _length;
|
|
32
|
-
this.offsets = [
|
|
33
|
-
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 : {});
|
|
46
|
+
}
|
|
47
|
+
get channelNames() {
|
|
48
|
+
var _a;
|
|
49
|
+
return Object.keys((_a = this.channels) !== null && _a !== void 0 ? _a : {});
|
|
34
50
|
}
|
|
35
|
-
getAtOffset(offset) {
|
|
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
|
+
}
|
|
36
57
|
(0, common_utils_1.assert)(offset >= 0 && offset < this._length, 0x443 /* Requested offset should be valid */);
|
|
37
|
-
return this.
|
|
58
|
+
return this.get(this.findIndex(offset));
|
|
38
59
|
}
|
|
39
60
|
findIndex(offset) {
|
|
40
61
|
// Note: maximum length here is 256 for text segments. Perf testing shows that linear scan beats binary search
|
|
@@ -46,6 +67,10 @@ class AttributionCollection {
|
|
|
46
67
|
}
|
|
47
68
|
return this.offsets[i] === offset ? i : i - 1;
|
|
48
69
|
}
|
|
70
|
+
get(index) {
|
|
71
|
+
const key = this.keys[index];
|
|
72
|
+
return key !== null ? key : undefined;
|
|
73
|
+
}
|
|
49
74
|
get length() {
|
|
50
75
|
return this._length;
|
|
51
76
|
}
|
|
@@ -54,12 +79,17 @@ class AttributionCollection {
|
|
|
54
79
|
*/
|
|
55
80
|
splitAt(pos) {
|
|
56
81
|
const splitIndex = this.findIndex(pos);
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
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));
|
|
61
85
|
splitCollection.keys.push(this.keys[i]);
|
|
62
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
|
+
}
|
|
63
93
|
const spliceIndex = this.offsets[splitIndex] === pos ? splitIndex : splitIndex + 1;
|
|
64
94
|
this.keys.splice(spliceIndex);
|
|
65
95
|
this.offsets.splice(spliceIndex);
|
|
@@ -67,6 +97,8 @@ class AttributionCollection {
|
|
|
67
97
|
return splitCollection;
|
|
68
98
|
}
|
|
69
99
|
append(other) {
|
|
100
|
+
var _a, _b, _c;
|
|
101
|
+
var _d;
|
|
70
102
|
const lastEntry = this.keys[this.keys.length - 1];
|
|
71
103
|
for (let i = 0; i < other.keys.length; i++) {
|
|
72
104
|
if (i !== 0 || !areEqualAttributionKeys(lastEntry, other.keys[i])) {
|
|
@@ -74,140 +106,162 @@ class AttributionCollection {
|
|
|
74
106
|
this.keys.push(other.keys[i]);
|
|
75
107
|
}
|
|
76
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
|
+
}
|
|
77
121
|
this._length += other.length;
|
|
78
122
|
}
|
|
79
123
|
getAll() {
|
|
80
|
-
const
|
|
124
|
+
const root = new Array(this.keys.length);
|
|
81
125
|
for (let i = 0; i < this.keys.length; i++) {
|
|
82
|
-
|
|
126
|
+
root[i] = { offset: this.offsets[i], key: this.keys[i] };
|
|
127
|
+
}
|
|
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
|
+
}
|
|
83
137
|
}
|
|
84
|
-
return
|
|
138
|
+
return result;
|
|
85
139
|
}
|
|
86
140
|
clone() {
|
|
87
|
-
const copy = new AttributionCollection(this.
|
|
141
|
+
const copy = new AttributionCollection(this.length);
|
|
88
142
|
copy.keys = this.keys.slice();
|
|
89
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
|
+
}
|
|
90
151
|
return copy;
|
|
91
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
|
+
}
|
|
92
170
|
/**
|
|
93
171
|
* Rehydrates attribution information from its serialized form into the provided iterable of consecutive segments.
|
|
94
172
|
*/
|
|
95
173
|
static populateAttributionCollections(segments, summary) {
|
|
96
|
-
const {
|
|
97
|
-
(0, common_utils_1.assert)(seqs.length === posBreakpoints.length
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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++;
|
|
111
192
|
}
|
|
112
|
-
|
|
193
|
+
if (attribution.offsets.length === 0) {
|
|
194
|
+
pushEntry(0, seqs[curIndex - 1]);
|
|
195
|
+
}
|
|
196
|
+
assignToSegment(attribution, segment);
|
|
197
|
+
cumulativeSegPos += segment.cachedLength;
|
|
113
198
|
}
|
|
114
|
-
|
|
115
|
-
|
|
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
|
+
});
|
|
116
212
|
}
|
|
117
|
-
segment.attribution = attribution;
|
|
118
|
-
cumulativeSegPos += segment.cachedLength;
|
|
119
213
|
}
|
|
120
214
|
}
|
|
121
215
|
/**
|
|
122
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.
|
|
123
220
|
*/
|
|
124
221
|
static serializeAttributionCollections(segments) {
|
|
125
|
-
var _a
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
let mostRecentAttributionKey;
|
|
129
|
-
let cumulativePos = 0;
|
|
130
|
-
let segmentsWithAttribution = 0;
|
|
131
|
-
let segmentsWithoutAttribution = 0;
|
|
222
|
+
var _a;
|
|
223
|
+
const allCollectionSpecs = [];
|
|
224
|
+
const allChannelNames = new Set();
|
|
132
225
|
for (const segment of segments) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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 ||
|
|
137
244
|
!areEqualAttributionKeys(key, mostRecentAttributionKey)) {
|
|
138
245
|
posBreakpoints.push(offset + cumulativePos);
|
|
139
|
-
seqs.push(key.type === "op" ? key.seq : key);
|
|
246
|
+
seqs.push(!key ? null : key.type === "op" ? key.seq : key);
|
|
140
247
|
}
|
|
141
248
|
mostRecentAttributionKey = key;
|
|
142
249
|
}
|
|
250
|
+
cumulativePos += spec.length;
|
|
143
251
|
}
|
|
144
|
-
|
|
145
|
-
|
|
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 };
|
|
146
260
|
}
|
|
147
|
-
|
|
261
|
+
blobContents.channels = channels;
|
|
148
262
|
}
|
|
149
|
-
(0, common_utils_1.assert)(segmentsWithAttribution === 0 || segmentsWithoutAttribution === 0, 0x446 /* Expected either all segments or no segments to have attribution information. */);
|
|
150
|
-
const blobContents = {
|
|
151
|
-
seqs,
|
|
152
|
-
posBreakpoints,
|
|
153
|
-
length: cumulativePos,
|
|
154
|
-
};
|
|
155
263
|
return blobContents;
|
|
156
264
|
}
|
|
157
265
|
}
|
|
158
266
|
exports.AttributionCollection = AttributionCollection;
|
|
159
|
-
/**
|
|
160
|
-
* @alpha
|
|
161
|
-
* @returns - An {@link AttributionPolicy} which tracks only insertion of content.
|
|
162
|
-
* Content is only attributed at ack time, unless the container is in a detached state.
|
|
163
|
-
* Detached content is attributed with a {@link @fluidframework/runtime-definitions#DetachedAttributionKey}.
|
|
164
|
-
*/
|
|
165
|
-
function createInsertOnlyAttributionPolicy() {
|
|
166
|
-
let unsubscribe;
|
|
167
|
-
return {
|
|
168
|
-
attach: (client) => {
|
|
169
|
-
(0, common_utils_1.assert)(unsubscribe === undefined, 0x557 /* cannot attach to multiple clients at once */);
|
|
170
|
-
const deltaCallback = (opArgs, { deltaSegments, operation }) => {
|
|
171
|
-
var _a;
|
|
172
|
-
if (operation !== ops_1.MergeTreeDeltaType.INSERT) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
for (const { segment } of deltaSegments) {
|
|
176
|
-
if (segment.seq !== undefined && segment.seq !== constants_1.UnassignedSequenceNumber) {
|
|
177
|
-
const key = segment.seq === constants_1.UniversalSequenceNumber
|
|
178
|
-
? { type: "detached", id: 0 }
|
|
179
|
-
: { type: "op", seq: segment.seq };
|
|
180
|
-
(_a = segment.attribution) !== null && _a !== void 0 ? _a : (segment.attribution = new AttributionCollection(key, segment.cachedLength));
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
const maintenanceCallback = ({ deltaSegments, operation }, opArgs) => {
|
|
185
|
-
if (operation !== mergeTreeDeltaCallback_1.MergeTreeMaintenanceType.ACKNOWLEDGED ||
|
|
186
|
-
opArgs === undefined ||
|
|
187
|
-
opArgs.op.type !== ops_1.MergeTreeDeltaType.INSERT) {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
for (const { segment } of deltaSegments) {
|
|
191
|
-
(0, common_utils_1.assert)(segment.seq !== undefined, 0x558 /* segment.seq should be set after ack. */);
|
|
192
|
-
segment.attribution = new AttributionCollection({ type: "op", seq: segment.seq }, segment.cachedLength);
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
client.on("delta", deltaCallback);
|
|
196
|
-
client.on("maintenance", maintenanceCallback);
|
|
197
|
-
unsubscribe = () => {
|
|
198
|
-
client.off("delta", deltaCallback);
|
|
199
|
-
client.off("maintenance", maintenanceCallback);
|
|
200
|
-
};
|
|
201
|
-
},
|
|
202
|
-
detach: () => {
|
|
203
|
-
unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe();
|
|
204
|
-
unsubscribe = undefined;
|
|
205
|
-
},
|
|
206
|
-
get isAttached() {
|
|
207
|
-
return unsubscribe !== undefined;
|
|
208
|
-
},
|
|
209
|
-
serializer: AttributionCollection,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
exports.createInsertOnlyAttributionPolicy = createInsertOnlyAttributionPolicy;
|
|
213
267
|
//# sourceMappingURL=attributionCollection.js.map
|