@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.
Files changed (88) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +20 -0
  3. package/dist/attributionCollection.d.ts +55 -22
  4. package/dist/attributionCollection.d.ts.map +1 -1
  5. package/dist/attributionCollection.js +166 -112
  6. package/dist/attributionCollection.js.map +1 -1
  7. package/dist/attributionPolicy.d.ts +36 -0
  8. package/dist/attributionPolicy.d.ts.map +1 -0
  9. package/dist/attributionPolicy.js +165 -0
  10. package/dist/attributionPolicy.js.map +1 -0
  11. package/dist/client.js +3 -3
  12. package/dist/client.js.map +1 -1
  13. package/dist/endOfTreeSegment.d.ts +4 -3
  14. package/dist/endOfTreeSegment.d.ts.map +1 -1
  15. package/dist/endOfTreeSegment.js +2 -12
  16. package/dist/endOfTreeSegment.js.map +1 -1
  17. package/dist/index.d.ts +3 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +2 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/mergeTree.d.ts +12 -7
  22. package/dist/mergeTree.d.ts.map +1 -1
  23. package/dist/mergeTree.js +86 -18
  24. package/dist/mergeTree.js.map +1 -1
  25. package/dist/mergeTreeTracking.d.ts +24 -4
  26. package/dist/mergeTreeTracking.d.ts.map +1 -1
  27. package/dist/mergeTreeTracking.js +48 -11
  28. package/dist/mergeTreeTracking.js.map +1 -1
  29. package/dist/revertibles.d.ts +23 -9
  30. package/dist/revertibles.d.ts.map +1 -1
  31. package/dist/revertibles.js +63 -43
  32. package/dist/revertibles.js.map +1 -1
  33. package/dist/segmentPropertiesManager.d.ts +1 -0
  34. package/dist/segmentPropertiesManager.d.ts.map +1 -1
  35. package/dist/segmentPropertiesManager.js +4 -0
  36. package/dist/segmentPropertiesManager.js.map +1 -1
  37. package/dist/snapshotLoader.js.map +1 -1
  38. package/dist/zamboni.js +2 -2
  39. package/dist/zamboni.js.map +1 -1
  40. package/lib/attributionCollection.d.ts +55 -22
  41. package/lib/attributionCollection.d.ts.map +1 -1
  42. package/lib/attributionCollection.js +165 -110
  43. package/lib/attributionCollection.js.map +1 -1
  44. package/lib/attributionPolicy.d.ts +36 -0
  45. package/lib/attributionPolicy.d.ts.map +1 -0
  46. package/lib/attributionPolicy.js +159 -0
  47. package/lib/attributionPolicy.js.map +1 -0
  48. package/lib/client.js +3 -3
  49. package/lib/client.js.map +1 -1
  50. package/lib/endOfTreeSegment.d.ts +4 -3
  51. package/lib/endOfTreeSegment.d.ts.map +1 -1
  52. package/lib/endOfTreeSegment.js +2 -12
  53. package/lib/endOfTreeSegment.js.map +1 -1
  54. package/lib/index.d.ts +3 -2
  55. package/lib/index.d.ts.map +1 -1
  56. package/lib/index.js +2 -2
  57. package/lib/index.js.map +1 -1
  58. package/lib/mergeTree.d.ts +12 -7
  59. package/lib/mergeTree.d.ts.map +1 -1
  60. package/lib/mergeTree.js +84 -17
  61. package/lib/mergeTree.js.map +1 -1
  62. package/lib/mergeTreeTracking.d.ts +24 -4
  63. package/lib/mergeTreeTracking.d.ts.map +1 -1
  64. package/lib/mergeTreeTracking.js +46 -10
  65. package/lib/mergeTreeTracking.js.map +1 -1
  66. package/lib/revertibles.d.ts +23 -9
  67. package/lib/revertibles.d.ts.map +1 -1
  68. package/lib/revertibles.js +64 -44
  69. package/lib/revertibles.js.map +1 -1
  70. package/lib/segmentPropertiesManager.d.ts +1 -0
  71. package/lib/segmentPropertiesManager.d.ts.map +1 -1
  72. package/lib/segmentPropertiesManager.js +4 -0
  73. package/lib/segmentPropertiesManager.js.map +1 -1
  74. package/lib/snapshotLoader.js.map +1 -1
  75. package/lib/zamboni.js +2 -2
  76. package/lib/zamboni.js.map +1 -1
  77. package/package.json +77 -17
  78. package/src/attributionCollection.ts +238 -157
  79. package/src/attributionPolicy.ts +250 -0
  80. package/src/client.ts +2 -2
  81. package/src/endOfTreeSegment.ts +3 -16
  82. package/src/index.ts +9 -2
  83. package/src/mergeTree.ts +122 -24
  84. package/src/mergeTreeTracking.ts +70 -17
  85. package/src/revertibles.ts +104 -47
  86. package/src/segmentPropertiesManager.ts +4 -0
  87. package/src/snapshotLoader.ts +1 -1
  88. package/src/zamboni.ts +2 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # @fluidframework/merge-tree
2
+
3
+ ## 2.0.0-internal.4.1.0
4
+
5
+ Dependency updates only.
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 SerializedAttributionCollection {
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): T;
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(): Iterable<{
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
- constructor(baseEntry: AttributionKey, _length: number);
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: Iterable<ISegment>, summary: SerializedAttributionCollection): void;
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,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAQhD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC/C;;;;;OAKG;IACH,IAAI,EAAE,CAAC,MAAM,GAAG,cAAc,CAAC,EAAE,CAAC;IAClC,cAAc,EAAE,MAAM,EAAE,CAAC;IAEzB,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,gCAAgC;IAChD;;OAEG;IACH,+BAA+B,CAC9B,QAAQ,EAAE,QAAQ,CAAC;QAClB,WAAW,CAAC,EAAE,sBAAsB,CAAC,cAAc,CAAC,CAAC;QACrD,YAAY,EAAE,MAAM,CAAC;KACrB,CAAC,GACA,+BAA+B,CAAC;IAEnC;;;OAGG;IACH,8BAA8B,CAC7B,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAC5B,OAAO,EAAE,+BAA+B,GACtC,IAAI,CAAC;CACR;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACxC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC;IAE/B;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB;;;;;;OAMG;IACH,MAAM,IAAI,QAAQ,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC;IAE/C,gBAAgB;IAChB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAEhD,gBAAgB;IAChB,MAAM,CAAC,KAAK,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAE/C,gBAAgB;IAChB,KAAK,IAAI,sBAAsB,CAAC,CAAC,CAAC,CAAC;CACnC;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,cAAc,GAAG,OAAO,CAgBrF;AAED,qBAAa,qBAAsB,YAAW,sBAAsB,CAAC,cAAc,CAAC;IAIrC,OAAO,CAAC,OAAO;IAH7D,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,IAAI,CAAmB;gBAEZ,SAAS,EAAE,cAAc,EAAU,OAAO,EAAE,MAAM;IAK9D,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc;IAKlD,OAAO,CAAC,SAAS;IAWjB,IAAW,MAAM,IAAI,MAAM,CAE1B;IAED;;OAEG;IACI,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB;IAgB3C,MAAM,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI;IAW1C,MAAM,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,cAAc,CAAA;KAAE,EAAE;IAQnD,KAAK,IAAI,qBAAqB;IAOrC;;OAEG;WACW,8BAA8B,CAC3C,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAC5B,OAAO,EAAE,+BAA+B,GACtC,IAAI;IAsCP;;OAEG;WACW,+BAA+B,CAC5C,QAAQ,EAAE,QAAQ,CAAC;QAClB,WAAW,CAAC,EAAE,sBAAsB,CAAC,cAAc,CAAC,CAAC;QACrD,YAAY,EAAE,MAAM,CAAC;KACrB,CAAC,GACA,+BAA+B;CAwClC;AAED;;;;;GAKG;AACH,wBAAgB,iCAAiC,IAAI,iBAAiB,CAsErE"}
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.createInsertOnlyAttributionPolicy = exports.AttributionCollection = exports.areEqualAttributionKeys = void 0;
7
+ exports.AttributionCollection = exports.areEqualAttributionKeys = void 0;
8
8
  const common_utils_1 = require("@fluidframework/common-utils");
9
- const constants_1 = require("./constants");
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(baseEntry, _length) {
34
+ constructor(_length, baseEntry) {
31
35
  this._length = _length;
32
- this.offsets = [0];
33
- this.keys = [baseEntry];
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.keys[this.findIndex(offset)];
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 splitBaseEntry = this.keys[splitIndex];
58
- const splitCollection = new AttributionCollection(splitBaseEntry, this.length - pos);
59
- for (let i = splitIndex + 1; i < this.keys.length; i++) {
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 results = new Array(this.keys.length);
124
+ const root = new Array(this.keys.length);
81
125
  for (let i = 0; i < this.keys.length; i++) {
82
- results[i] = { offset: this.offsets[i], key: this.keys[i] };
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 results;
138
+ return result;
85
139
  }
86
140
  clone() {
87
- const copy = new AttributionCollection(this.keys[0], this.length);
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 { seqs, posBreakpoints } = summary;
97
- (0, common_utils_1.assert)(seqs.length === posBreakpoints.length && seqs.length > 0, 0x445 /* Invalid attribution summary blob provided */);
98
- let curIndex = 0;
99
- let currentInfo = seqs[curIndex];
100
- let cumulativeSegPos = 0;
101
- for (const segment of segments) {
102
- const attribution = new AttributionCollection(typeof currentInfo === "object" ? currentInfo : { type: "op", seq: currentInfo }, segment.cachedLength);
103
- while (posBreakpoints[curIndex] < cumulativeSegPos + segment.cachedLength) {
104
- currentInfo = seqs[curIndex];
105
- const nextOffset = posBreakpoints[curIndex] - cumulativeSegPos;
106
- if (attribution.offsets[attribution.offsets.length - 1] !== nextOffset) {
107
- attribution.offsets.push(nextOffset);
108
- attribution.keys.push(typeof currentInfo === "object"
109
- ? currentInfo
110
- : { type: "op", seq: currentInfo });
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
- curIndex++;
193
+ if (attribution.offsets.length === 0) {
194
+ pushEntry(0, seqs[curIndex - 1]);
195
+ }
196
+ assignToSegment(attribution, segment);
197
+ cumulativeSegPos += segment.cachedLength;
113
198
  }
114
- if (posBreakpoints[curIndex] === cumulativeSegPos + segment.cachedLength) {
115
- currentInfo = seqs[curIndex];
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, _b;
126
- const posBreakpoints = [];
127
- const seqs = [];
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
- if (segment.attribution) {
134
- segmentsWithAttribution++;
135
- for (const { offset, key } of (_b = (_a = segment.attribution) === null || _a === void 0 ? void 0 : _a.getAll()) !== null && _b !== void 0 ? _b : []) {
136
- if (!mostRecentAttributionKey ||
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
- else {
145
- segmentsWithoutAttribution++;
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
- cumulativePos += segment.cachedLength;
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