@fluidframework/tree 2.112.0 → 2.113.0-411909

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 (192) hide show
  1. package/api-report/tree.alpha.api.md +23 -2
  2. package/dist/entrypoints/alpha.d.ts +1 -1
  3. package/dist/entrypoints/alpha.d.ts.map +1 -1
  4. package/dist/entrypoints/alpha.js +3 -2
  5. package/dist/entrypoints/alpha.js.map +1 -1
  6. package/dist/feature-libraries/default-schema/defaultFieldKinds.js +1 -1
  7. package/dist/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
  8. package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts +39 -7
  9. package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
  10. package/dist/feature-libraries/modular-schema/fieldChangeHandler.js +22 -2
  11. package/dist/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
  12. package/dist/feature-libraries/modular-schema/genericFieldKind.js +1 -1
  13. package/dist/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  14. package/dist/feature-libraries/modular-schema/index.d.ts +1 -1
  15. package/dist/feature-libraries/modular-schema/index.d.ts.map +1 -1
  16. package/dist/feature-libraries/modular-schema/index.js +2 -1
  17. package/dist/feature-libraries/modular-schema/index.js.map +1 -1
  18. package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  19. package/dist/feature-libraries/modular-schema/modularChangeFamily.js +8 -1
  20. package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  21. package/dist/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
  22. package/dist/feature-libraries/optional-field/optionalField.js +26 -3
  23. package/dist/feature-libraries/optional-field/optionalField.js.map +1 -1
  24. package/dist/feature-libraries/sequence-field/filterEdits.d.ts +12 -0
  25. package/dist/feature-libraries/sequence-field/filterEdits.d.ts.map +1 -0
  26. package/dist/feature-libraries/sequence-field/filterEdits.js +111 -0
  27. package/dist/feature-libraries/sequence-field/filterEdits.js.map +1 -0
  28. package/dist/feature-libraries/sequence-field/markQueue.d.ts +8 -3
  29. package/dist/feature-libraries/sequence-field/markQueue.d.ts.map +1 -1
  30. package/dist/feature-libraries/sequence-field/markQueue.js +22 -8
  31. package/dist/feature-libraries/sequence-field/markQueue.js.map +1 -1
  32. package/dist/feature-libraries/sequence-field/moveEffectTable.d.ts +1 -1
  33. package/dist/feature-libraries/sequence-field/moveEffectTable.d.ts.map +1 -1
  34. package/dist/feature-libraries/sequence-field/moveEffectTable.js +2 -6
  35. package/dist/feature-libraries/sequence-field/moveEffectTable.js.map +1 -1
  36. package/dist/feature-libraries/sequence-field/sequenceFieldChangeRebaser.d.ts +7 -6
  37. package/dist/feature-libraries/sequence-field/sequenceFieldChangeRebaser.d.ts.map +1 -1
  38. package/dist/feature-libraries/sequence-field/sequenceFieldChangeRebaser.js +2 -4
  39. package/dist/feature-libraries/sequence-field/sequenceFieldChangeRebaser.js.map +1 -1
  40. package/dist/index.d.ts +1 -1
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +2 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/packageVersion.d.ts +1 -1
  45. package/dist/packageVersion.d.ts.map +1 -1
  46. package/dist/packageVersion.js +1 -1
  47. package/dist/packageVersion.js.map +1 -1
  48. package/dist/shared-tree/schematizingTreeView.d.ts +8 -0
  49. package/dist/shared-tree/schematizingTreeView.d.ts.map +1 -1
  50. package/dist/shared-tree/schematizingTreeView.js +25 -13
  51. package/dist/shared-tree/schematizingTreeView.js.map +1 -1
  52. package/dist/simple-tree/api/configuration.d.ts +32 -2
  53. package/dist/simple-tree/api/configuration.d.ts.map +1 -1
  54. package/dist/simple-tree/api/configuration.js +3 -1
  55. package/dist/simple-tree/api/configuration.js.map +1 -1
  56. package/dist/simple-tree/api/index.d.ts +1 -1
  57. package/dist/simple-tree/api/index.d.ts.map +1 -1
  58. package/dist/simple-tree/api/index.js.map +1 -1
  59. package/dist/simple-tree/api/schemaCompatibilityTester.d.ts +7 -4
  60. package/dist/simple-tree/api/schemaCompatibilityTester.d.ts.map +1 -1
  61. package/dist/simple-tree/api/schemaCompatibilityTester.js +7 -5
  62. package/dist/simple-tree/api/schemaCompatibilityTester.js.map +1 -1
  63. package/dist/simple-tree/api/tree.d.ts +58 -1
  64. package/dist/simple-tree/api/tree.d.ts.map +1 -1
  65. package/dist/simple-tree/api/tree.js.map +1 -1
  66. package/dist/simple-tree/core/index.d.ts +1 -1
  67. package/dist/simple-tree/core/index.d.ts.map +1 -1
  68. package/dist/simple-tree/core/index.js +2 -1
  69. package/dist/simple-tree/core/index.js.map +1 -1
  70. package/dist/simple-tree/core/toStored.d.ts +72 -6
  71. package/dist/simple-tree/core/toStored.d.ts.map +1 -1
  72. package/dist/simple-tree/core/toStored.js +25 -1
  73. package/dist/simple-tree/core/toStored.js.map +1 -1
  74. package/dist/simple-tree/index.d.ts +3 -3
  75. package/dist/simple-tree/index.d.ts.map +1 -1
  76. package/dist/simple-tree/index.js +4 -5
  77. package/dist/simple-tree/index.js.map +1 -1
  78. package/dist/simple-tree/prepareForInsertion.d.ts.map +1 -1
  79. package/dist/simple-tree/prepareForInsertion.js +9 -0
  80. package/dist/simple-tree/prepareForInsertion.js.map +1 -1
  81. package/dist/simple-tree/toStoredSchema.d.ts +11 -27
  82. package/dist/simple-tree/toStoredSchema.d.ts.map +1 -1
  83. package/dist/simple-tree/toStoredSchema.js +25 -33
  84. package/dist/simple-tree/toStoredSchema.js.map +1 -1
  85. package/docs/main/runtime-schema-upgrade.md +208 -0
  86. package/lib/entrypoints/alpha.d.ts +1 -1
  87. package/lib/entrypoints/alpha.d.ts.map +1 -1
  88. package/lib/entrypoints/alpha.js +1 -1
  89. package/lib/entrypoints/alpha.js.map +1 -1
  90. package/lib/feature-libraries/default-schema/defaultFieldKinds.js +1 -1
  91. package/lib/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
  92. package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts +39 -7
  93. package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
  94. package/lib/feature-libraries/modular-schema/fieldChangeHandler.js +21 -1
  95. package/lib/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
  96. package/lib/feature-libraries/modular-schema/genericFieldKind.js +1 -1
  97. package/lib/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  98. package/lib/feature-libraries/modular-schema/index.d.ts +1 -1
  99. package/lib/feature-libraries/modular-schema/index.d.ts.map +1 -1
  100. package/lib/feature-libraries/modular-schema/index.js +1 -1
  101. package/lib/feature-libraries/modular-schema/index.js.map +1 -1
  102. package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  103. package/lib/feature-libraries/modular-schema/modularChangeFamily.js +9 -2
  104. package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  105. package/lib/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
  106. package/lib/feature-libraries/optional-field/optionalField.js +27 -4
  107. package/lib/feature-libraries/optional-field/optionalField.js.map +1 -1
  108. package/lib/feature-libraries/sequence-field/filterEdits.d.ts +12 -0
  109. package/lib/feature-libraries/sequence-field/filterEdits.d.ts.map +1 -0
  110. package/lib/feature-libraries/sequence-field/filterEdits.js +107 -0
  111. package/lib/feature-libraries/sequence-field/filterEdits.js.map +1 -0
  112. package/lib/feature-libraries/sequence-field/markQueue.d.ts +8 -3
  113. package/lib/feature-libraries/sequence-field/markQueue.d.ts.map +1 -1
  114. package/lib/feature-libraries/sequence-field/markQueue.js +22 -9
  115. package/lib/feature-libraries/sequence-field/markQueue.js.map +1 -1
  116. package/lib/feature-libraries/sequence-field/moveEffectTable.d.ts +1 -1
  117. package/lib/feature-libraries/sequence-field/moveEffectTable.d.ts.map +1 -1
  118. package/lib/feature-libraries/sequence-field/moveEffectTable.js +2 -6
  119. package/lib/feature-libraries/sequence-field/moveEffectTable.js.map +1 -1
  120. package/lib/feature-libraries/sequence-field/sequenceFieldChangeRebaser.d.ts +7 -6
  121. package/lib/feature-libraries/sequence-field/sequenceFieldChangeRebaser.d.ts.map +1 -1
  122. package/lib/feature-libraries/sequence-field/sequenceFieldChangeRebaser.js +2 -4
  123. package/lib/feature-libraries/sequence-field/sequenceFieldChangeRebaser.js.map +1 -1
  124. package/lib/index.d.ts +1 -1
  125. package/lib/index.d.ts.map +1 -1
  126. package/lib/index.js +1 -1
  127. package/lib/index.js.map +1 -1
  128. package/lib/packageVersion.d.ts +1 -1
  129. package/lib/packageVersion.d.ts.map +1 -1
  130. package/lib/packageVersion.js +1 -1
  131. package/lib/packageVersion.js.map +1 -1
  132. package/lib/shared-tree/schematizingTreeView.d.ts +8 -0
  133. package/lib/shared-tree/schematizingTreeView.d.ts.map +1 -1
  134. package/lib/shared-tree/schematizingTreeView.js +26 -14
  135. package/lib/shared-tree/schematizingTreeView.js.map +1 -1
  136. package/lib/simple-tree/api/configuration.d.ts +32 -2
  137. package/lib/simple-tree/api/configuration.d.ts.map +1 -1
  138. package/lib/simple-tree/api/configuration.js +4 -2
  139. package/lib/simple-tree/api/configuration.js.map +1 -1
  140. package/lib/simple-tree/api/index.d.ts +1 -1
  141. package/lib/simple-tree/api/index.d.ts.map +1 -1
  142. package/lib/simple-tree/api/index.js.map +1 -1
  143. package/lib/simple-tree/api/schemaCompatibilityTester.d.ts +7 -4
  144. package/lib/simple-tree/api/schemaCompatibilityTester.d.ts.map +1 -1
  145. package/lib/simple-tree/api/schemaCompatibilityTester.js +7 -5
  146. package/lib/simple-tree/api/schemaCompatibilityTester.js.map +1 -1
  147. package/lib/simple-tree/api/tree.d.ts +58 -1
  148. package/lib/simple-tree/api/tree.d.ts.map +1 -1
  149. package/lib/simple-tree/api/tree.js.map +1 -1
  150. package/lib/simple-tree/core/index.d.ts +1 -1
  151. package/lib/simple-tree/core/index.d.ts.map +1 -1
  152. package/lib/simple-tree/core/index.js +1 -1
  153. package/lib/simple-tree/core/index.js.map +1 -1
  154. package/lib/simple-tree/core/toStored.d.ts +72 -6
  155. package/lib/simple-tree/core/toStored.d.ts.map +1 -1
  156. package/lib/simple-tree/core/toStored.js +24 -0
  157. package/lib/simple-tree/core/toStored.js.map +1 -1
  158. package/lib/simple-tree/index.d.ts +3 -3
  159. package/lib/simple-tree/index.d.ts.map +1 -1
  160. package/lib/simple-tree/index.js +2 -2
  161. package/lib/simple-tree/index.js.map +1 -1
  162. package/lib/simple-tree/prepareForInsertion.d.ts.map +1 -1
  163. package/lib/simple-tree/prepareForInsertion.js +9 -0
  164. package/lib/simple-tree/prepareForInsertion.js.map +1 -1
  165. package/lib/simple-tree/toStoredSchema.d.ts +11 -27
  166. package/lib/simple-tree/toStoredSchema.d.ts.map +1 -1
  167. package/lib/simple-tree/toStoredSchema.js +24 -33
  168. package/lib/simple-tree/toStoredSchema.js.map +1 -1
  169. package/package.json +21 -21
  170. package/src/entrypoints/alpha.ts +3 -0
  171. package/src/feature-libraries/default-schema/defaultFieldKinds.ts +1 -1
  172. package/src/feature-libraries/modular-schema/fieldChangeHandler.ts +51 -7
  173. package/src/feature-libraries/modular-schema/genericFieldKind.ts +1 -1
  174. package/src/feature-libraries/modular-schema/index.ts +4 -2
  175. package/src/feature-libraries/modular-schema/modularChangeFamily.ts +16 -1
  176. package/src/feature-libraries/optional-field/optionalField.ts +43 -3
  177. package/src/feature-libraries/sequence-field/filterEdits.ts +143 -0
  178. package/src/feature-libraries/sequence-field/markQueue.ts +28 -11
  179. package/src/feature-libraries/sequence-field/moveEffectTable.ts +2 -7
  180. package/src/feature-libraries/sequence-field/sequenceFieldChangeRebaser.ts +3 -5
  181. package/src/index.ts +3 -0
  182. package/src/packageVersion.ts +1 -1
  183. package/src/shared-tree/schematizingTreeView.ts +42 -20
  184. package/src/simple-tree/api/configuration.ts +40 -2
  185. package/src/simple-tree/api/index.ts +1 -0
  186. package/src/simple-tree/api/schemaCompatibilityTester.ts +8 -4
  187. package/src/simple-tree/api/tree.ts +58 -1
  188. package/src/simple-tree/core/index.ts +2 -1
  189. package/src/simple-tree/core/toStored.ts +95 -8
  190. package/src/simple-tree/index.ts +3 -2
  191. package/src/simple-tree/prepareForInsertion.ts +12 -0
  192. package/src/simple-tree/toStoredSchema.ts +44 -39
@@ -5,6 +5,7 @@
5
5
 
6
6
  import type { ICodecFamily, JsonCodecPart } from "../../codec/index.js";
7
7
  import type {
8
+ ChangeAtomId,
8
9
  ChangeEncodingContext,
9
10
  DeltaDetachedNodeChanges,
10
11
  DeltaDetachedNodeId,
@@ -16,7 +17,7 @@ import type {
16
17
  RevisionTag,
17
18
  RevisionTagSchema,
18
19
  } from "../../core/index.js";
19
- import type { IdAllocator, Invariant } from "../../util/index.js";
20
+ import type { IdAllocator, Invariant, RangeQueryResult } from "../../util/index.js";
20
21
 
21
22
  import type { CrossFieldManager } from "./crossFieldQueries.js";
22
23
  import type { EncodedNodeChangeset } from "./modularChangeFormatV1.js";
@@ -167,10 +168,53 @@ export interface FieldChangeRebaser<TChangeset> {
167
168
  replaceRevisions(change: TChangeset, replacer: RevisionReplacer): TChangeset;
168
169
 
169
170
  /**
170
- * Returns a copy of the given changeset with the same declarations (e.g., new cells) but no actual changes.
171
- * This is a kludge. TODO: remove once AB#46104 is completed.
171
+ * Returns a copy of the given changeset with edits removed as specified.
172
+ * @param change - The change to filter edits from
173
+ * @param filterDetach - This should be called for each range of detaches in the changeset,
174
+ * and the detach should be preserved, removed, or converted to a non-move detach as specified.
175
+ * If the returned result does not cover the entire detach range, the remainder should be queried again.
176
+ * @param filterDetach - This should be called for each range of attaches in the changeset,
177
+ * and the attach should be preserved, removed, or converted to a non-move attach as specified.
178
+ * If the returned result does not cover the entire detach range, the remainder should be queried again.
179
+ * @param preserveOtherEdits - Whether edits other than attaches and detaches (e.g. root renames),
180
+ * should be preserved or removed.
172
181
  */
173
- mute(change: TChangeset): TChangeset;
182
+ filterEdits(
183
+ change: TChangeset,
184
+ options: {
185
+ filterDetach: EditFilterFunc;
186
+ filterAttach: EditFilterFunc;
187
+ preserveOtherEdits: boolean;
188
+ },
189
+ ): TChangeset;
190
+ }
191
+
192
+ export type EditFilterFunc = (
193
+ id: ChangeAtomId,
194
+ count: number,
195
+ endpoint?: ChangeAtomId,
196
+ ) => RangeQueryResult<EditFilterStatus>;
197
+
198
+ /**
199
+ * Used to describe what should be done with a particular attach or detach during `filterEdits`.
200
+ */
201
+ export enum EditFilterStatus {
202
+ /**
203
+ * The edit should be removed from the filtered changeset.
204
+ */
205
+ Remove,
206
+
207
+ /**
208
+ * The edit should be preserved in the filtered changeset.
209
+ */
210
+ Preserve,
211
+
212
+ /**
213
+ * This should only be used for an attach or detach which is part of a move.
214
+ * The edit should be preserved, but should be adjusted, if necessary,
215
+ * to reflect that the other endpoint of the move has been filtered out.
216
+ */
217
+ PreserveWithoutMove,
174
218
  }
175
219
 
176
220
  /**
@@ -181,13 +225,13 @@ export function referenceFreeFieldChangeRebaser<TChangeset>(data: {
181
225
  compose: (change1: TChangeset, change2: TChangeset) => TChangeset;
182
226
  invert: (change: TChangeset) => TChangeset;
183
227
  rebase: (change: TChangeset, over: TChangeset) => TChangeset;
184
- mute: (change: TChangeset) => TChangeset;
228
+ filterEdits: FieldChangeRebaser<TChangeset>["filterEdits"];
185
229
  }): FieldChangeRebaser<TChangeset> {
186
230
  return isolatedFieldChangeRebaser({
187
231
  compose: (change1, change2, _composeChild, _genId) => data.compose(change1, change2),
188
232
  invert: (change, _invertChild, _genId) => data.invert(change),
189
233
  rebase: (change, over, _rebaseChild, _genId) => data.rebase(change, over),
190
- mute: (change) => data.mute(change),
234
+ filterEdits: (change, options) => data.filterEdits(change, options),
191
235
  });
192
236
  }
193
237
 
@@ -195,7 +239,7 @@ export function isolatedFieldChangeRebaser<TChangeset>(data: {
195
239
  compose: FieldChangeRebaser<TChangeset>["compose"];
196
240
  invert: FieldChangeRebaser<TChangeset>["invert"];
197
241
  rebase: FieldChangeRebaser<TChangeset>["rebase"];
198
- mute: FieldChangeRebaser<TChangeset>["mute"];
242
+ filterEdits: FieldChangeRebaser<TChangeset>["filterEdits"];
199
243
  }): FieldChangeRebaser<TChangeset> {
200
244
  return {
201
245
  ...data,
@@ -40,7 +40,7 @@ export const genericChangeHandler: FieldChangeHandler<GenericChangeset> = {
40
40
  rebase: rebaseGenericChange,
41
41
  prune: pruneGenericChange,
42
42
  replaceRevisions,
43
- mute: (change: GenericChangeset): GenericChangeset => change,
43
+ filterEdits: (change: GenericChangeset): GenericChangeset => change,
44
44
  },
45
45
  codecsFactory: makeGenericChangeCodec,
46
46
  editor: {
@@ -29,10 +29,14 @@ export {
29
29
  export { EncodedModularChangesetV2 } from "./modularChangeFormatV2.js";
30
30
  export { FlexFieldKind, type FullSchemaPolicy } from "./fieldKind.js";
31
31
  export {
32
+ type EditFilterFunc,
33
+ EditFilterStatus,
32
34
  type FieldChangeHandler,
33
35
  type FieldChangeDelta,
36
+ type FieldChangeEncodingContext,
34
37
  type FieldChangeRebaser,
35
38
  type FieldEditor,
39
+ type NestedChangesIndices,
36
40
  type NodeChangeComposer,
37
41
  type NodeChangeInverter,
38
42
  type NodeChangeRebaser,
@@ -42,8 +46,6 @@ export {
42
46
  type RelevantRemovedRootsFromChild,
43
47
  type ToDelta,
44
48
  NodeAttachState,
45
- type FieldChangeEncodingContext,
46
- type NestedChangesIndices,
47
49
  } from "./fieldChangeHandler.js";
48
50
  export type {
49
51
  CrossFieldKey,
@@ -82,6 +82,7 @@ import {
82
82
  setInCrossFieldMap,
83
83
  } from "./crossFieldQueries.js";
84
84
  import {
85
+ EditFilterStatus,
85
86
  type FieldChangeHandler,
86
87
  NodeAttachState,
87
88
  type RebaseRevisionMetadata,
@@ -1739,11 +1740,25 @@ export class ModularChangeFamily
1739
1740
  const handler = getChangeHandler(this.fieldKinds, change.fieldKind);
1740
1741
  return {
1741
1742
  fieldKind: change.fieldKind,
1742
- change: brand(handler.rebaser.mute(change.change)),
1743
+ change: brand(
1744
+ handler.rebaser.filterEdits(change.change, {
1745
+ filterDetach: removeAllEditsFilter,
1746
+ filterAttach: removeAllEditsFilter,
1747
+ preserveOtherEdits: false,
1748
+ }),
1749
+ ),
1743
1750
  };
1744
1751
  }
1745
1752
  }
1746
1753
 
1754
+ function removeAllEditsFilter(
1755
+ _id: ChangeAtomId,
1756
+ count: number,
1757
+ _endpointId?: ChangeAtomId,
1758
+ ): RangeQueryResult<EditFilterStatus> {
1759
+ return { value: EditFilterStatus.Remove, length: count };
1760
+ }
1761
+
1747
1762
  function replaceCrossFieldKeyTableRevisions(
1748
1763
  table: CrossFieldKeyTable,
1749
1764
  replacer: RevisionReplacer,
@@ -35,6 +35,7 @@ import {
35
35
  requiredIdentifier,
36
36
  } from "../fieldKindIdentifiers.js";
37
37
  import {
38
+ EditFilterStatus,
38
39
  type FieldChangeHandler,
39
40
  type FieldChangeRebaser,
40
41
  type FieldEditor,
@@ -48,6 +49,7 @@ import {
48
49
  type NestedChangesIndices,
49
50
  type FieldChangeDelta,
50
51
  FlexFieldKind,
52
+ type EditFilterFunc,
51
53
  } from "../modular-schema/index.js";
52
54
 
53
55
  import type {
@@ -466,9 +468,7 @@ export const optionalChangeRebaser: FieldChangeRebaser<OptionalChangeset> = {
466
468
  return updated;
467
469
  },
468
470
 
469
- mute: (change: OptionalChangeset): OptionalChangeset => {
470
- return { childChanges: change.childChanges, moves: [] };
471
- },
471
+ filterEdits,
472
472
  };
473
473
 
474
474
  function replaceReplaceRevisions(
@@ -814,3 +814,43 @@ export const optional: Optional = new FlexFieldKind(
814
814
  ]),
815
815
  },
816
816
  );
817
+
818
+ function filterEdits(
819
+ change: OptionalChangeset,
820
+ options: {
821
+ filterDetach: EditFilterFunc;
822
+ filterAttach: EditFilterFunc;
823
+ preserveOtherEdits: boolean;
824
+ },
825
+ ): OptionalChangeset {
826
+ const filtered: Mutable<OptionalChangeset> = { ...change };
827
+ if (filtered.valueReplace !== undefined) {
828
+ if (isReplaceEffectful(filtered.valueReplace)) {
829
+ const detachId = getEffectfulDst(filtered.valueReplace);
830
+ const detachResult =
831
+ detachId === undefined ? undefined : options.filterDetach(detachId, 1).value;
832
+
833
+ const attachId = filtered.valueReplace.src;
834
+ const attachResult =
835
+ attachId === undefined ? undefined : options.filterAttach(attachId, 1).value;
836
+
837
+ if (detachResult === EditFilterStatus.Remove) {
838
+ assert(
839
+ attachId === undefined || attachResult === EditFilterStatus.Remove,
840
+ "Cannot remove detach without also removing attach",
841
+ );
842
+
843
+ delete filtered.valueReplace;
844
+ } else if (attachResult === EditFilterStatus.Remove) {
845
+ delete filtered.valueReplace.src;
846
+ }
847
+ } else if (!options.preserveOtherEdits) {
848
+ delete filtered.valueReplace;
849
+ }
850
+ }
851
+
852
+ if (!options.preserveOtherEdits) {
853
+ filtered.moves = [];
854
+ }
855
+ return filtered;
856
+ }
@@ -0,0 +1,143 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import type { ChangeAtomId } from "../../core/index.js";
7
+ import type { RangeQueryResult } from "../../util/index.js";
8
+ import { EditFilterStatus, type EditFilterFunc } from "../modular-schema/index.js";
9
+ import { MarkListFactory } from "./markListFactory.js";
10
+ import { NoopMarkType, type Changeset, type Mark } from "./types.js";
11
+ import { getDetachedNodeId, getDetachOutputCellId, omitMarkEffect } from "./utils.js";
12
+ import { MarkQueueBase } from "./markQueue.js";
13
+ import { unreachableCase } from "@fluidframework/core-utils/internal";
14
+
15
+ export function filterEdits(
16
+ change: Changeset,
17
+ options: {
18
+ filterDetach: EditFilterFunc;
19
+ filterAttach: EditFilterFunc;
20
+ preserveOtherEdits: boolean;
21
+ },
22
+ ): Changeset {
23
+ const factory = new MarkListFactory();
24
+ const queue = new MarkQueueBase(change);
25
+ for (let mark = queue.peek(); mark !== undefined; mark = queue.peek()) {
26
+ const filtered = filterMark(
27
+ mark,
28
+ options.filterDetach,
29
+ options.filterAttach,
30
+ options.preserveOtherEdits,
31
+ );
32
+ factory.push(filtered);
33
+ queue.dequeueUpTo(filtered.count);
34
+ }
35
+
36
+ return factory.list;
37
+ }
38
+
39
+ function filterMark(
40
+ mark: Mark,
41
+ filterDetach: (
42
+ id: ChangeAtomId,
43
+ count: number,
44
+ endpoint?: ChangeAtomId,
45
+ ) => RangeQueryResult<EditFilterStatus>,
46
+ filterAttach: (
47
+ id: ChangeAtomId,
48
+ count: number,
49
+ endpoint?: ChangeAtomId,
50
+ ) => RangeQueryResult<EditFilterStatus>,
51
+ preserveOtherEdits: boolean,
52
+ ): Mark {
53
+ const type = mark.type;
54
+ switch (type) {
55
+ case "Insert":
56
+ case "MoveIn": {
57
+ if (mark.cellId === undefined) {
58
+ return preserveOtherEdits ? mark : omitMarkEffect(mark);
59
+ }
60
+
61
+ const endpoint = mark.type === "MoveIn" ? mark.finalEndpoint : undefined;
62
+ const result = filterAttach(mark.cellId, mark.count, endpoint);
63
+
64
+ let filtered: Mark;
65
+ switch (result.value) {
66
+ case EditFilterStatus.Preserve: {
67
+ filtered = mark;
68
+ break;
69
+ }
70
+ case EditFilterStatus.PreserveWithoutMove: {
71
+ // KLUDGE: PreserveWithoutMove requires us to return a mark which attaches the node
72
+ // using the detach ID of the move (the endpoint ID).
73
+ // Insert marks use the cell ID as the attach ID, so we must change the cell ID here.
74
+ // This could be a problem if there were references to the old cell ID outside this changeset,
75
+ // but PreserveWithoutMove is only used for transaction minimization, where that is not a problem.
76
+ const newCellId = endpoint ?? { revision: mark.revision, localId: mark.id };
77
+ filtered = {
78
+ type: "Insert",
79
+ count: result.length,
80
+ cellId: newCellId,
81
+ revision: mark.revision,
82
+ id: mark.id,
83
+ };
84
+ break;
85
+ }
86
+ case EditFilterStatus.Remove: {
87
+ filtered = omitMarkEffect(mark);
88
+ break;
89
+ }
90
+ default: {
91
+ unreachableCase(result.value);
92
+ }
93
+ }
94
+
95
+ return { ...filtered, count: result.length };
96
+ }
97
+ case "Remove":
98
+ case "MoveOut": {
99
+ const detachId = getDetachedNodeId(mark);
100
+ const endpoint = mark.type === "MoveOut" ? mark.finalEndpoint : undefined;
101
+ const result = filterDetach(detachId, mark.count, endpoint);
102
+
103
+ let filtered: Mark;
104
+ switch (result.value) {
105
+ case EditFilterStatus.Preserve: {
106
+ filtered = mark;
107
+ break;
108
+ }
109
+ case EditFilterStatus.PreserveWithoutMove: {
110
+ const outputCellId = getDetachOutputCellId(mark);
111
+ filtered = {
112
+ type: "Remove",
113
+ count: mark.count,
114
+ revision: outputCellId.revision,
115
+ id: outputCellId.localId,
116
+ };
117
+
118
+ if (mark.changes !== undefined) {
119
+ filtered.changes = mark.changes;
120
+ }
121
+
122
+ break;
123
+ }
124
+ case EditFilterStatus.Remove: {
125
+ filtered = omitMarkEffect(mark);
126
+ break;
127
+ }
128
+ default: {
129
+ unreachableCase(result.value);
130
+ }
131
+ }
132
+ return { ...filtered, count: result.length };
133
+ }
134
+ case NoopMarkType:
135
+ case "Rename":
136
+ case "AttachAndDetach": {
137
+ return preserveOtherEdits ? mark : omitMarkEffect(mark);
138
+ }
139
+ default: {
140
+ unreachableCase(type);
141
+ }
142
+ }
143
+ }
@@ -3,20 +3,17 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { assert, oob } from "@fluidframework/core-utils/internal";
6
+ import { assert } from "@fluidframework/core-utils/internal";
7
7
 
8
- import { type MoveEffectTable, splitMarkForMoveEffects } from "./moveEffectTable.js";
8
+ import { getFirstMoveEffectLength, type MoveEffectTable } from "./moveEffectTable.js";
9
9
  import type { Mark } from "./types.js";
10
10
  import { splitMark } from "./utils.js";
11
11
 
12
- export class MarkQueue {
12
+ export class MarkQueueBase {
13
13
  private readonly stack: Mark[] = [];
14
14
  private index = 0;
15
15
 
16
- public constructor(
17
- private readonly list: readonly Mark[],
18
- private readonly moveEffects: MoveEffectTable,
19
- ) {
16
+ public constructor(private readonly list: readonly Mark[]) {
20
17
  this.list = list;
21
18
  }
22
19
 
@@ -36,11 +33,14 @@ export class MarkQueue {
36
33
  return undefined;
37
34
  }
38
35
 
39
- const splitMarks = splitMarkForMoveEffects(mark, this.moveEffects);
40
- for (let i = splitMarks.length - 1; i > 0; i--) {
41
- this.stack.push(splitMarks[i] ?? oob());
36
+ const splitLength = this.getLengthForSplit(mark);
37
+ if (splitLength < mark.count) {
38
+ const [part1, part2] = splitMark(mark, splitLength);
39
+ this.stack.push(part2);
40
+ return part1;
42
41
  }
43
- return splitMarks[0];
42
+
43
+ return mark;
44
44
  }
45
45
 
46
46
  /**
@@ -66,4 +66,21 @@ export class MarkQueue {
66
66
  }
67
67
  return mark;
68
68
  }
69
+
70
+ protected getLengthForSplit(mark: Mark): number {
71
+ return mark.count;
72
+ }
73
+ }
74
+
75
+ export class MarkQueue extends MarkQueueBase {
76
+ public constructor(
77
+ list: readonly Mark[],
78
+ private readonly moveEffects: MoveEffectTable,
79
+ ) {
80
+ super(list);
81
+ }
82
+
83
+ protected override getLengthForSplit(mark: Mark): number {
84
+ return getFirstMoveEffectLength(mark, mark.count, this.moveEffects);
85
+ }
69
86
  }
@@ -15,7 +15,7 @@ import {
15
15
 
16
16
  import type { MoveMarkEffect } from "./helperTypes.js";
17
17
  import type { CellMark, Detach, Mark, MarkEffect, MoveId, MoveIn, MoveOut } from "./types.js";
18
- import { isAttachAndDetachEffect, splitMark, splitMarkEffect } from "./utils.js";
18
+ import { isAttachAndDetachEffect, splitMarkEffect } from "./utils.js";
19
19
 
20
20
  export type MoveEffectTable = CrossFieldManager<MoveEffect>;
21
21
 
@@ -168,12 +168,7 @@ function adjustMoveEffectBasis(effect: MoveEffectWithBasis, newBasis: MoveId): M
168
168
  return adjusted;
169
169
  }
170
170
 
171
- export function splitMarkForMoveEffects(mark: Mark, effects: MoveEffectTable): Mark[] {
172
- const length = getFirstMoveEffectLength(mark, mark.count, effects);
173
- return length < mark.count ? splitMark(mark, length) : [mark];
174
- }
175
-
176
- function getFirstMoveEffectLength(
171
+ export function getFirstMoveEffectLength(
177
172
  markEffect: MarkEffect,
178
173
  count: number,
179
174
  effects: MoveEffectTable,
@@ -6,12 +6,12 @@
6
6
  import type { FieldChangeRebaser } from "../modular-schema/index.js";
7
7
 
8
8
  import { compose } from "./compose.js";
9
+ import { filterEdits } from "./filterEdits.js";
9
10
  import { invert } from "./invert.js";
10
11
  import { prune } from "./prune.js";
11
12
  import { rebase } from "./rebase.js";
12
13
  import { replaceRevisions } from "./replaceRevisions.js";
13
14
  import type { Changeset } from "./types.js";
14
- import { omitMarkEffect } from "./utils.js";
15
15
 
16
16
  export type SequenceChangeRebaser = FieldChangeRebaser<Changeset>;
17
17
 
@@ -21,7 +21,5 @@ export const sequenceFieldChangeRebaser = {
21
21
  rebase,
22
22
  prune,
23
23
  replaceRevisions,
24
- mute: (change: Changeset): Changeset => {
25
- return change.map((mark) => omitMarkEffect(mark));
26
- },
27
- };
24
+ filterEdits,
25
+ } as const;
package/src/index.ts CHANGED
@@ -99,6 +99,7 @@ export {
99
99
  type TreeNodeSchema,
100
100
  TreeViewConfiguration,
101
101
  type ITreeViewConfiguration,
102
+ type ITreeViewConfigurationAlpha,
102
103
  type ITreeConfigurationOptions,
103
104
  type TreeView,
104
105
  type TreeViewEvents,
@@ -334,6 +335,8 @@ export {
334
335
  type ErasedNode,
335
336
  type ErasedSchemaSubclassable,
336
337
  type SnapshotSchemaCompatibilityOptions,
338
+ StagedSchemaUpgradePolicy,
339
+ type StagedSchemaUpgradePolicyFactory,
337
340
  type ArrayPlaceAnchor,
338
341
  createArrayInsertionAnchor,
339
342
  type WithValue,
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/tree";
9
- export const pkgVersion = "2.112.0";
9
+ export const pkgVersion = "2.113.0-411909";
@@ -23,6 +23,7 @@ import {
23
23
  type FlexTreeUnknownUnboxed,
24
24
  FieldKinds,
25
25
  type FlexTreeRequiredField,
26
+ allowsRepoSuperset,
26
27
  } from "../feature-libraries/index.js";
27
28
  import {
28
29
  type ImplicitFieldSchema,
@@ -34,6 +35,7 @@ import {
34
35
  normalizeFieldSchema,
35
36
  checkSchemaCompatibility,
36
37
  type InsertableContent,
38
+ type StagedSchemaUpgradePolicy,
37
39
  type TreeViewConfiguration,
38
40
  type TreeViewAlpha,
39
41
  type InsertableField,
@@ -99,7 +101,14 @@ export class SchematizingSimpleTreeView<
99
101
  IEmitter<TreeViewEvents & TreeBranchEvents> &
100
102
  HasListeners<TreeViewEvents & TreeBranchEvents> = createEmitter();
101
103
 
104
+ /**
105
+ * The schema for this view, captured at construction time for compatibility checking.
106
+ */
102
107
  private readonly viewSchema: TreeSchema;
108
+ /**
109
+ * Stored-schema generation policy from the view configuration, frozen at construction time.
110
+ */
111
+ private readonly stagedUpgradePolicy: StagedSchemaUpgradePolicy;
103
112
 
104
113
  /**
105
114
  * Events to unregister upon flex-tree view disposal.
@@ -142,8 +151,17 @@ export class SchematizingSimpleTreeView<
142
151
 
143
152
  this.rootFieldSchema = normalizeFieldSchema(config.schema);
144
153
 
145
- const configAlpha = new TreeViewConfigurationAlpha({ schema: config.schema });
154
+ const stagedUpgradePolicy =
155
+ config instanceof TreeViewConfigurationAlpha ? config.stagedUpgradePolicy : undefined;
156
+ const configAlpha = new TreeViewConfigurationAlpha({
157
+ schema: config.schema,
158
+ enableSchemaValidation: config.enableSchemaValidation,
159
+ preventAmbiguity: config.preventAmbiguity,
160
+ stagedUpgradePolicy,
161
+ });
162
+ this.stagedUpgradePolicy = configAlpha.stagedUpgradePolicy;
146
163
 
164
+ // Store viewSchema directly from the configuration (TreeViewConfigurationAlpha implements TreeSchema)
147
165
  this.viewSchema = configAlpha;
148
166
  // This must be initialized before `update` can be called.
149
167
  this.currentCompatibility = {
@@ -189,7 +207,7 @@ export class SchematizingSimpleTreeView<
189
207
  }
190
208
 
191
209
  this.runSchemaEdit(() => {
192
- const schema = toInitialSchema(this.config.schema);
210
+ const schema = toInitialSchema(this.config.schema, this.stagedUpgradePolicy);
193
211
  // This has to be the contextless version, since when "initialize" is called (right after this),
194
212
  // it will do a schema change which would dispose of the current context (see inside `update`).
195
213
  // Thus using the current context (if any) would hydrate nodes then
@@ -247,19 +265,18 @@ export class SchematizingSimpleTreeView<
247
265
  public upgradeSchema(): void {
248
266
  this.ensureUndisposed();
249
267
 
250
- const compatibility = this.compatibility;
251
- if (compatibility.isEquivalent) {
252
- // No-op
253
- return;
254
- }
255
-
256
- if (!compatibility.canUpgrade) {
268
+ const newSchema = toUpgradeSchema(this.viewSchema.root, this.stagedUpgradePolicy);
269
+ const storedSchema = this.checkout.storedSchema.clone();
270
+ if (!allowsRepoSuperset(defaultSchemaPolicy, storedSchema, newSchema)) {
257
271
  throw new UsageError(
258
- "Existing stored schema cannot be upgraded (see TreeView.compatibility.canUpgrade).",
272
+ "Existing stored schema cannot be upgraded to the requested schema (see TreeView.compatibility.canUpgrade).",
259
273
  );
260
274
  }
275
+ if (allowsRepoSuperset(defaultSchemaPolicy, newSchema, storedSchema)) {
276
+ // No-op
277
+ return;
278
+ }
261
279
 
262
- const newSchema = toUpgradeSchema(this.viewSchema.root);
263
280
  this.runSchemaEdit(() => this.checkout.updateSchema(newSchema));
264
281
  }
265
282
 
@@ -348,15 +365,8 @@ export class SchematizingSimpleTreeView<
348
365
  private update(): void {
349
366
  this.disposeFlexView();
350
367
 
351
- const compatibility = checkSchemaCompatibility(
352
- this.viewSchema,
353
- this.checkout.storedSchema,
354
- );
355
-
356
- this.currentCompatibility = {
357
- ...compatibility,
358
- canInitialize: canInitialize(this.checkout),
359
- };
368
+ const compatibility = this.computeCompatibility();
369
+ this.currentCompatibility = compatibility;
360
370
 
361
371
  const anchors = this.checkout.forest.anchors;
362
372
  const slots = anchors.slots;
@@ -427,6 +437,18 @@ export class SchematizingSimpleTreeView<
427
437
  }
428
438
  }
429
439
 
440
+ private computeCompatibility(): SchemaCompatibilityStatus {
441
+ const compatibility = checkSchemaCompatibility(
442
+ this.viewSchema,
443
+ this.checkout.storedSchema,
444
+ this.stagedUpgradePolicy,
445
+ );
446
+ return {
447
+ ...compatibility,
448
+ canInitialize: canInitialize(this.checkout),
449
+ };
450
+ }
451
+
430
452
  private runSchemaEdit(edit: () => void): void {
431
453
  this.midUpgrade = true;
432
454
  try {