@fluidframework/tree 2.10.0-306579 → 2.10.0-307399

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 (110) hide show
  1. package/api-report/tree.alpha.api.md +11 -9
  2. package/api-report/tree.beta.api.md +2 -0
  3. package/api-report/tree.legacy.alpha.api.md +2 -0
  4. package/api-report/tree.legacy.public.api.md +2 -0
  5. package/api-report/tree.public.api.md +2 -0
  6. package/dist/feature-libraries/chunked-forest/basicChunk.d.ts +26 -5
  7. package/dist/feature-libraries/chunked-forest/basicChunk.d.ts.map +1 -1
  8. package/dist/feature-libraries/chunked-forest/basicChunk.js +15 -5
  9. package/dist/feature-libraries/chunked-forest/basicChunk.js.map +1 -1
  10. package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  11. package/dist/feature-libraries/chunked-forest/chunkedForest.js +5 -0
  12. package/dist/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  13. package/dist/feature-libraries/index.d.ts +1 -1
  14. package/dist/feature-libraries/index.d.ts.map +1 -1
  15. package/dist/feature-libraries/index.js +2 -2
  16. package/dist/feature-libraries/index.js.map +1 -1
  17. package/dist/feature-libraries/modular-schema/discrepancies.d.ts +27 -27
  18. package/dist/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -1
  19. package/dist/feature-libraries/modular-schema/discrepancies.js +152 -193
  20. package/dist/feature-libraries/modular-schema/discrepancies.js.map +1 -1
  21. package/dist/feature-libraries/modular-schema/index.d.ts +1 -1
  22. package/dist/feature-libraries/modular-schema/index.d.ts.map +1 -1
  23. package/dist/feature-libraries/modular-schema/index.js +2 -2
  24. package/dist/feature-libraries/modular-schema/index.js.map +1 -1
  25. package/dist/packageVersion.d.ts +1 -1
  26. package/dist/packageVersion.js +1 -1
  27. package/dist/packageVersion.js.map +1 -1
  28. package/dist/shared-tree/treeApi.js +4 -1
  29. package/dist/shared-tree/treeApi.js.map +1 -1
  30. package/dist/simple-tree/api/schemaCreationUtilities.d.ts +12 -14
  31. package/dist/simple-tree/api/schemaCreationUtilities.d.ts.map +1 -1
  32. package/dist/simple-tree/api/schemaCreationUtilities.js +9 -7
  33. package/dist/simple-tree/api/schemaCreationUtilities.js.map +1 -1
  34. package/dist/simple-tree/api/schemaFactory.d.ts +2 -0
  35. package/dist/simple-tree/api/schemaFactory.d.ts.map +1 -1
  36. package/dist/simple-tree/api/schemaFactory.js +4 -1
  37. package/dist/simple-tree/api/schemaFactory.js.map +1 -1
  38. package/dist/simple-tree/api/schemaFactoryRecursive.js.map +1 -1
  39. package/dist/simple-tree/core/treeNodeKernel.d.ts +4 -5
  40. package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  41. package/dist/simple-tree/core/treeNodeKernel.js +63 -67
  42. package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
  43. package/dist/simple-tree/objectNode.d.ts +1 -1
  44. package/dist/simple-tree/objectNode.js.map +1 -1
  45. package/dist/simple-tree/objectNodeTypes.d.ts +3 -0
  46. package/dist/simple-tree/objectNodeTypes.d.ts.map +1 -1
  47. package/dist/simple-tree/objectNodeTypes.js +3 -1
  48. package/dist/simple-tree/objectNodeTypes.js.map +1 -1
  49. package/docs/.attachments/object-merge-semantics.drawio +145 -0
  50. package/docs/user-facing/array-merge-semantics.md +344 -0
  51. package/docs/user-facing/map-merge-semantics.md +128 -0
  52. package/docs/user-facing/merge-semantics.md +7 -3
  53. package/docs/user-facing/object-merge-semantics.md +77 -0
  54. package/lib/feature-libraries/chunked-forest/basicChunk.d.ts +26 -5
  55. package/lib/feature-libraries/chunked-forest/basicChunk.d.ts.map +1 -1
  56. package/lib/feature-libraries/chunked-forest/basicChunk.js +15 -5
  57. package/lib/feature-libraries/chunked-forest/basicChunk.js.map +1 -1
  58. package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  59. package/lib/feature-libraries/chunked-forest/chunkedForest.js +5 -0
  60. package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  61. package/lib/feature-libraries/index.d.ts +1 -1
  62. package/lib/feature-libraries/index.d.ts.map +1 -1
  63. package/lib/feature-libraries/index.js +1 -1
  64. package/lib/feature-libraries/index.js.map +1 -1
  65. package/lib/feature-libraries/modular-schema/discrepancies.d.ts +27 -27
  66. package/lib/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -1
  67. package/lib/feature-libraries/modular-schema/discrepancies.js +150 -191
  68. package/lib/feature-libraries/modular-schema/discrepancies.js.map +1 -1
  69. package/lib/feature-libraries/modular-schema/index.d.ts +1 -1
  70. package/lib/feature-libraries/modular-schema/index.d.ts.map +1 -1
  71. package/lib/feature-libraries/modular-schema/index.js +1 -1
  72. package/lib/feature-libraries/modular-schema/index.js.map +1 -1
  73. package/lib/packageVersion.d.ts +1 -1
  74. package/lib/packageVersion.js +1 -1
  75. package/lib/packageVersion.js.map +1 -1
  76. package/lib/shared-tree/treeApi.js +5 -2
  77. package/lib/shared-tree/treeApi.js.map +1 -1
  78. package/lib/simple-tree/api/schemaCreationUtilities.d.ts +12 -14
  79. package/lib/simple-tree/api/schemaCreationUtilities.d.ts.map +1 -1
  80. package/lib/simple-tree/api/schemaCreationUtilities.js +9 -7
  81. package/lib/simple-tree/api/schemaCreationUtilities.js.map +1 -1
  82. package/lib/simple-tree/api/schemaFactory.d.ts +2 -0
  83. package/lib/simple-tree/api/schemaFactory.d.ts.map +1 -1
  84. package/lib/simple-tree/api/schemaFactory.js +4 -1
  85. package/lib/simple-tree/api/schemaFactory.js.map +1 -1
  86. package/lib/simple-tree/api/schemaFactoryRecursive.js.map +1 -1
  87. package/lib/simple-tree/core/treeNodeKernel.d.ts +4 -5
  88. package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  89. package/lib/simple-tree/core/treeNodeKernel.js +64 -68
  90. package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
  91. package/lib/simple-tree/objectNode.d.ts +1 -1
  92. package/lib/simple-tree/objectNode.js.map +1 -1
  93. package/lib/simple-tree/objectNodeTypes.d.ts +3 -0
  94. package/lib/simple-tree/objectNodeTypes.d.ts.map +1 -1
  95. package/lib/simple-tree/objectNodeTypes.js +3 -1
  96. package/lib/simple-tree/objectNodeTypes.js.map +1 -1
  97. package/package.json +23 -31
  98. package/src/feature-libraries/chunked-forest/basicChunk.ts +12 -4
  99. package/src/feature-libraries/chunked-forest/chunkedForest.ts +5 -0
  100. package/src/feature-libraries/index.ts +1 -1
  101. package/src/feature-libraries/modular-schema/discrepancies.ts +202 -241
  102. package/src/feature-libraries/modular-schema/index.ts +4 -1
  103. package/src/packageVersion.ts +1 -1
  104. package/src/shared-tree/treeApi.ts +7 -5
  105. package/src/simple-tree/api/schemaCreationUtilities.ts +29 -17
  106. package/src/simple-tree/api/schemaFactory.ts +25 -18
  107. package/src/simple-tree/api/schemaFactoryRecursive.ts +1 -1
  108. package/src/simple-tree/core/treeNodeKernel.ts +62 -64
  109. package/src/simple-tree/objectNode.ts +1 -1
  110. package/src/simple-tree/objectNodeTypes.ts +3 -1
@@ -6,7 +6,6 @@
6
6
  import { assert } from "@fluidframework/core-utils/internal";
7
7
 
8
8
  import {
9
- type FieldKey,
10
9
  type FieldKindIdentifier,
11
10
  LeafNodeStoredSchema,
12
11
  MapNodeStoredSchema,
@@ -14,6 +13,7 @@ import {
14
13
  storedEmptyFieldSchema,
15
14
  type TreeFieldStoredSchema,
16
15
  type TreeNodeSchemaIdentifier,
16
+ type TreeNodeStoredSchema,
17
17
  type TreeStoredSchema,
18
18
  type TreeTypeSet,
19
19
  type ValueSchema,
@@ -27,45 +27,45 @@ import { brand } from "../../util/index.js";
27
27
  /**
28
28
  * @remarks
29
29
  *
30
- * 1. FieldIncompatibility
30
+ * 1. FieldDiscrepancy
31
31
  *
32
- * `FieldIncompatibility` represents the differences between two `TreeFieldStoredSchema` objects. It consists of
32
+ * `FieldDiscrepancy` represents the differences between two `TreeFieldStoredSchema` objects. It consists of
33
33
  * three types of incompatibilities:
34
34
  *
35
- * - FieldKindIncompatibility: Indicates the differences in `FieldKindIdentifier` between two `TreeFieldStoredSchema`
35
+ * - FieldKindDiscrepancy: Indicates the differences in `FieldKindIdentifier` between two `TreeFieldStoredSchema`
36
36
  * objects (e.g., optional, required, sequence, etc.).
37
- * - AllowedTypesIncompatibility: Indicates the differences in the allowed child types between the two schemas.
38
- * - ValueSchemaIncompatibility: Specifically indicates the differences in the `ValueSchema` of two
37
+ * - AllowedTypesDiscrepancy: Indicates the differences in the allowed child types between the two schemas.
38
+ * - ValueSchemaDiscrepancy: Specifically indicates the differences in the `ValueSchema` of two
39
39
  * `LeafNodeStoredSchema` objects.
40
40
  *
41
- * 2. NodeIncompatibility
41
+ * 2. NodeDiscrepancy
42
42
  *
43
- * `NodeIncompatibility` represents the differences between two `TreeNodeStoredSchema` objects and includes:
43
+ * `NodeDiscrepancy` represents the differences between two `TreeNodeStoredSchema` objects and includes:
44
44
  *
45
- * - NodeKindIncompatibility: Indicates the differences in the types of `TreeNodeStoredSchema` (currently supports
45
+ * - NodeKindDiscrepancy: Indicates the differences in the types of `TreeNodeStoredSchema` (currently supports
46
46
  * `ObjectNodeStoredSchema`, `MapNodeStoredSchema`, and `LeafNodeStoredSchema`).
47
- * - NodeFieldsIncompatibility: Indicates the `FieldIncompatibility` of `TreeFieldStoredSchema` within two
48
- * `TreeNodeStoredSchema`. It includes an array of `FieldIncompatibility` instances in the `differences` field.
47
+ * - NodeFieldsDiscrepancy: Indicates the `FieldDiscrepancy` of `TreeFieldStoredSchema` within two
48
+ * `TreeNodeStoredSchema`. It includes an array of `FieldDiscrepancy` instances in the `differences` field.
49
49
  *
50
50
  * When comparing two nodes for compatibility, it only makes sense to compare their fields if the nodes are of
51
51
  * the same kind (map, object, leaf).
52
52
  *
53
- * 3. Incompatibility
53
+ * 3. Discrepancy
54
54
  *
55
- * Incompatibility consists of both `NodeIncompatibility` and `FieldIncompatibility`, representing any kind of
56
- * schema differences. See {@link getAllowedContentIncompatibilities} for more details about how we process it
55
+ * Discrepancy consists of both `NodeDiscrepancy` and `FieldDiscrepancy`, representing any kind of
56
+ * schema differences. See {@link getAllowedContentDiscrepancies} for more details about how we process it
57
57
  * and the ordering.
58
58
  */
59
- export type Incompatibility = FieldIncompatibility | NodeIncompatibility;
59
+ export type Discrepancy = FieldDiscrepancy | NodeDiscrepancy;
60
60
 
61
- export type NodeIncompatibility = NodeKindIncompatibility | NodeFieldsIncompatibility;
61
+ export type NodeDiscrepancy = NodeKindDiscrepancy | NodeFieldsDiscrepancy;
62
62
 
63
- export type FieldIncompatibility =
64
- | AllowedTypeIncompatibility
65
- | FieldKindIncompatibility
66
- | ValueSchemaIncompatibility;
63
+ export type FieldDiscrepancy =
64
+ | AllowedTypeDiscrepancy
65
+ | FieldKindDiscrepancy
66
+ | ValueSchemaDiscrepancy;
67
67
 
68
- export interface AllowedTypeIncompatibility {
68
+ export interface AllowedTypeDiscrepancy {
69
69
  identifier: string | undefined; // undefined indicates root field schema
70
70
  mismatch: "allowedTypes";
71
71
  /**
@@ -78,213 +78,159 @@ export interface AllowedTypeIncompatibility {
78
78
  stored: string[];
79
79
  }
80
80
 
81
- export interface FieldKindIncompatibility {
81
+ export interface FieldKindDiscrepancy {
82
82
  identifier: string | undefined; // undefined indicates root field schema
83
83
  mismatch: "fieldKind";
84
84
  view: FieldKindIdentifier;
85
85
  stored: FieldKindIdentifier;
86
86
  }
87
87
 
88
- export interface ValueSchemaIncompatibility {
88
+ export interface ValueSchemaDiscrepancy {
89
89
  identifier: string;
90
90
  mismatch: "valueSchema";
91
91
  view: ValueSchema | undefined;
92
92
  stored: ValueSchema | undefined;
93
93
  }
94
94
 
95
- export interface NodeKindIncompatibility {
95
+ export interface NodeKindDiscrepancy {
96
96
  identifier: string;
97
97
  mismatch: "nodeKind";
98
98
  view: SchemaFactoryNodeKind | undefined;
99
99
  stored: SchemaFactoryNodeKind | undefined;
100
100
  }
101
101
 
102
- export interface NodeFieldsIncompatibility {
102
+ export interface NodeFieldsDiscrepancy {
103
103
  identifier: string;
104
104
  mismatch: "fields";
105
- differences: FieldIncompatibility[];
105
+ differences: FieldDiscrepancy[];
106
106
  }
107
107
 
108
108
  type SchemaFactoryNodeKind = "object" | "leaf" | "map";
109
109
 
110
+ function getNodeSchemaType(nodeSchema: TreeNodeStoredSchema): SchemaFactoryNodeKind {
111
+ if (nodeSchema instanceof ObjectNodeStoredSchema) {
112
+ return "object";
113
+ } else if (nodeSchema instanceof MapNodeStoredSchema) {
114
+ return "map";
115
+ } else if (nodeSchema instanceof LeafNodeStoredSchema) {
116
+ return "leaf";
117
+ }
118
+ throwUnsupportedNodeType(nodeSchema.constructor.name);
119
+ }
120
+
110
121
  /**
111
- * @remarks
122
+ * Finds and reports discrepancies between a view schema and a stored schema.
112
123
  *
113
124
  * The workflow for finding schema incompatibilities:
114
- * 1. Compare the two root schemas to identify any `FieldIncompatibility`.
125
+ * 1. Compare the two root schemas to identify any `FieldDiscrepancy`.
115
126
  *
116
127
  * 2. For each node schema in the `view`:
117
128
  * - Verify if the node schema exists in the stored. If it does, ensure that the `SchemaFactoryNodeKind` are
118
- * consistent. Otherwise this difference is treated as `NodeKindIncompatibility`
129
+ * consistent. Otherwise this difference is treated as `NodeKindDiscrepancy`
119
130
  * - If a node schema with the same identifier exists in both view and stored, and their `SchemaFactoryNodeKind`
120
- * are consistent, perform a exhaustive validation to identify all `FieldIncompatibility`.
131
+ * are consistent, perform a exhaustive validation to identify all `FieldDiscrepancy`.
121
132
  *
122
133
  * 3. For each node schema in the stored, verify if it exists in the view. The overlapping parts were already
123
134
  * addressed in the previous step.
124
135
  *
125
136
  * @returns the discrepancies between two TreeStoredSchema objects
126
137
  */
127
- export function getAllowedContentIncompatibilities(
138
+ export function* getAllowedContentDiscrepancies(
128
139
  view: TreeStoredSchema,
129
140
  stored: TreeStoredSchema,
130
- ): Incompatibility[] {
131
- const incompatibilities: Incompatibility[] = [];
132
-
141
+ ): Iterable<Discrepancy> {
133
142
  // check root schema discrepancies
134
- incompatibilities.push(
135
- ...trackFieldDiscrepancies(view.rootFieldSchema, stored.rootFieldSchema),
136
- );
137
-
138
- // Verify the existence and type of a node schema given its identifier (key), then determine if
139
- // an exhaustive search is necessary.
140
- const viewNodeKeys = new Set<TreeNodeSchemaIdentifier>();
141
- for (const [key, viewNodeSchema] of view.nodeSchema) {
142
- viewNodeKeys.add(key);
143
-
144
- if (viewNodeSchema instanceof ObjectNodeStoredSchema) {
145
- if (!stored.nodeSchema.has(key)) {
146
- incompatibilities.push({
147
- identifier: key,
143
+ yield* getFieldDiscrepancies(view.rootFieldSchema, stored.rootFieldSchema);
144
+
145
+ for (const result of compareMaps(view.nodeSchema, stored.nodeSchema)) {
146
+ switch (result.type) {
147
+ case "aExtra": {
148
+ const viewType = getNodeSchemaType(result.value);
149
+ yield {
150
+ identifier: result.key,
148
151
  mismatch: "nodeKind",
149
- view: "object",
152
+ view: viewType,
150
153
  stored: undefined,
151
- });
152
- } else {
153
- const storedNodeSchema = stored.nodeSchema.get(key);
154
- assert(
155
- storedNodeSchema !== undefined,
156
- 0x9be /* The storedNodeSchema in stored.nodeSchema should not be undefined */,
157
- );
158
- if (storedNodeSchema instanceof MapNodeStoredSchema) {
159
- incompatibilities.push({
160
- identifier: key,
161
- mismatch: "nodeKind",
162
- view: "object",
163
- stored: "map",
164
- } satisfies NodeKindIncompatibility);
165
- } else if (storedNodeSchema instanceof LeafNodeStoredSchema) {
166
- incompatibilities.push({
167
- identifier: key,
168
- mismatch: "nodeKind",
169
- view: "object",
170
- stored: "leaf",
171
- } satisfies NodeKindIncompatibility);
172
- } else if (storedNodeSchema instanceof ObjectNodeStoredSchema) {
173
- const differences = trackObjectNodeDiscrepancies(viewNodeSchema, storedNodeSchema);
174
- if (differences.length > 0) {
175
- incompatibilities.push({
176
- identifier: key,
177
- mismatch: "fields",
178
- differences,
179
- } satisfies NodeFieldsIncompatibility);
180
- }
181
- } else {
182
- throwUnsupportedNodeType(storedNodeSchema.constructor.name);
183
- }
154
+ };
155
+ break;
184
156
  }
185
- } else if (viewNodeSchema instanceof MapNodeStoredSchema) {
186
- if (!stored.nodeSchema.has(key)) {
187
- incompatibilities.push({
188
- identifier: key,
157
+ case "bExtra": {
158
+ const storedType = getNodeSchemaType(result.value);
159
+ yield {
160
+ identifier: result.key,
189
161
  mismatch: "nodeKind",
190
- view: "map",
191
- stored: undefined,
192
- } satisfies NodeKindIncompatibility);
193
- } else {
194
- const storedNodeSchema = stored.nodeSchema.get(key);
195
- assert(
196
- storedNodeSchema !== undefined,
197
- 0x9bf /* The storedNodeSchema in stored.nodeSchema should not be undefined */,
198
- );
199
- if (storedNodeSchema instanceof ObjectNodeStoredSchema) {
200
- incompatibilities.push({
201
- identifier: key,
202
- mismatch: "nodeKind",
203
- view: "map",
204
- stored: "object",
205
- } satisfies NodeKindIncompatibility);
206
- } else if (storedNodeSchema instanceof LeafNodeStoredSchema) {
207
- incompatibilities.push({
208
- identifier: key,
209
- mismatch: "nodeKind",
210
- view: "map",
211
- stored: "leaf",
212
- } satisfies NodeKindIncompatibility);
213
- } else if (storedNodeSchema instanceof MapNodeStoredSchema) {
214
- incompatibilities.push(
215
- ...trackFieldDiscrepancies(
216
- viewNodeSchema.mapFields,
217
- storedNodeSchema.mapFields,
218
- key,
219
- ),
220
- );
221
- } else {
222
- throwUnsupportedNodeType(storedNodeSchema.constructor.name);
223
- }
162
+ view: undefined,
163
+ stored: storedType,
164
+ };
165
+ break;
224
166
  }
225
- } else if (viewNodeSchema instanceof LeafNodeStoredSchema) {
226
- if (!stored.nodeSchema.has(key)) {
227
- incompatibilities.push({
228
- identifier: key,
229
- mismatch: "nodeKind",
230
- view: "leaf",
231
- stored: undefined,
232
- });
233
- } else {
234
- const storedNodeSchema = stored.nodeSchema.get(key);
235
- assert(
236
- storedNodeSchema !== undefined,
237
- 0x9c0 /* The storedNodeSchema in stored.nodeSchema should not be undefined */,
238
- );
239
- if (storedNodeSchema instanceof MapNodeStoredSchema) {
240
- incompatibilities.push({
241
- identifier: key,
242
- mismatch: "nodeKind",
243
- view: "leaf",
244
- stored: "map",
245
- } satisfies NodeKindIncompatibility);
246
- } else if (storedNodeSchema instanceof ObjectNodeStoredSchema) {
247
- incompatibilities.push({
248
- identifier: key,
249
- mismatch: "nodeKind",
250
- view: "leaf",
251
- stored: "object",
252
- } satisfies NodeKindIncompatibility);
253
- } else if (storedNodeSchema instanceof LeafNodeStoredSchema) {
254
- if (viewNodeSchema.leafValue !== storedNodeSchema.leafValue) {
255
- incompatibilities.push({
256
- identifier: key,
257
- mismatch: "valueSchema",
258
- view: viewNodeSchema.leafValue,
259
- stored: storedNodeSchema.leafValue,
260
- } satisfies ValueSchemaIncompatibility);
261
- }
262
- } else {
263
- throwUnsupportedNodeType(storedNodeSchema.constructor.name);
264
- }
167
+ case "both": {
168
+ yield* getNodeDiscrepancies(result.key, result.valueA, result.valueB);
169
+ break;
265
170
  }
266
- } else {
267
- throwUnsupportedNodeType(viewNodeSchema.constructor.name);
171
+ default:
172
+ break;
268
173
  }
269
174
  }
175
+ }
270
176
 
271
- for (const [key, storedNodeSchema] of stored.nodeSchema) {
272
- if (!viewNodeKeys.has(key)) {
273
- incompatibilities.push({
274
- identifier: key,
275
- mismatch: "nodeKind",
276
- view: undefined,
277
- stored:
278
- storedNodeSchema instanceof MapNodeStoredSchema
279
- ? "map"
280
- : storedNodeSchema instanceof ObjectNodeStoredSchema
281
- ? "object"
282
- : "leaf",
283
- } satisfies NodeKindIncompatibility);
284
- }
177
+ function* getNodeDiscrepancies(
178
+ identifier: TreeNodeSchemaIdentifier,
179
+ view: TreeNodeStoredSchema,
180
+ stored: TreeNodeStoredSchema,
181
+ ): Iterable<Discrepancy> {
182
+ const viewType = getNodeSchemaType(view);
183
+ const storedType = getNodeSchemaType(stored);
184
+ if (viewType !== storedType) {
185
+ yield {
186
+ identifier,
187
+ mismatch: "nodeKind",
188
+ view: viewType,
189
+ stored: storedType,
190
+ };
191
+ return;
285
192
  }
286
193
 
287
- return incompatibilities;
194
+ switch (viewType) {
195
+ case "object": {
196
+ const differences = Array.from(
197
+ trackObjectNodeDiscrepancies(
198
+ view as ObjectNodeStoredSchema,
199
+ stored as ObjectNodeStoredSchema,
200
+ ),
201
+ );
202
+ if (differences.length > 0) {
203
+ yield {
204
+ identifier,
205
+ mismatch: "fields",
206
+ differences,
207
+ } satisfies NodeFieldsDiscrepancy;
208
+ }
209
+ break;
210
+ }
211
+ case "map":
212
+ yield* getFieldDiscrepancies(
213
+ (view as MapNodeStoredSchema).mapFields,
214
+ (stored as MapNodeStoredSchema).mapFields,
215
+ identifier,
216
+ );
217
+ break;
218
+ case "leaf": {
219
+ const viewValue = (view as LeafNodeStoredSchema).leafValue;
220
+ const storedValue = (stored as LeafNodeStoredSchema).leafValue;
221
+ if (viewValue !== storedValue) {
222
+ yield {
223
+ identifier,
224
+ mismatch: "valueSchema",
225
+ view: viewValue,
226
+ stored: storedValue,
227
+ };
228
+ }
229
+ break;
230
+ }
231
+ default:
232
+ break;
233
+ }
288
234
  }
289
235
 
290
236
  /**
@@ -292,13 +238,11 @@ export function getAllowedContentIncompatibilities(
292
238
  *
293
239
  * @param keyOrRoot - If the key is missing, it indicates that this is the root field schema.
294
240
  */
295
- function trackFieldDiscrepancies(
241
+ function* getFieldDiscrepancies(
296
242
  view: TreeFieldStoredSchema,
297
243
  stored: TreeFieldStoredSchema,
298
244
  keyOrRoot?: string,
299
- ): FieldIncompatibility[] {
300
- const differences: FieldIncompatibility[] = [];
301
-
245
+ ): Iterable<FieldDiscrepancy> {
302
246
  // Only track the symmetric differences of two sets.
303
247
  const findSetDiscrepancies = (
304
248
  a: TreeTypeSet,
@@ -309,34 +253,30 @@ function trackFieldDiscrepancies(
309
253
  return [aDiff, bDiff];
310
254
  };
311
255
 
312
- const allowedTypesDiscrepancies = findSetDiscrepancies(view.types, stored.types);
313
- if (allowedTypesDiscrepancies[0].length > 0 || allowedTypesDiscrepancies[1].length > 0) {
314
- differences.push({
256
+ const [viewExtra, storedExtra] = findSetDiscrepancies(view.types, stored.types);
257
+ if (viewExtra.length > 0 || storedExtra.length > 0) {
258
+ yield {
315
259
  identifier: keyOrRoot,
316
260
  mismatch: "allowedTypes",
317
- view: allowedTypesDiscrepancies[0],
318
- stored: allowedTypesDiscrepancies[1],
319
- } satisfies AllowedTypeIncompatibility);
261
+ view: viewExtra,
262
+ stored: storedExtra,
263
+ } satisfies AllowedTypeDiscrepancy;
320
264
  }
321
265
 
322
266
  if (view.kind !== stored.kind) {
323
- differences.push({
267
+ yield {
324
268
  identifier: keyOrRoot,
325
269
  mismatch: "fieldKind",
326
270
  view: view.kind,
327
271
  stored: stored.kind,
328
- } satisfies FieldKindIncompatibility);
272
+ } satisfies FieldKindDiscrepancy;
329
273
  }
330
-
331
- return differences;
332
274
  }
333
275
 
334
- function trackObjectNodeDiscrepancies(
276
+ function* trackObjectNodeDiscrepancies(
335
277
  view: ObjectNodeStoredSchema,
336
278
  stored: ObjectNodeStoredSchema,
337
- ): FieldIncompatibility[] {
338
- const differences: FieldIncompatibility[] = [];
339
- const viewFieldKeys = new Set<FieldKey>();
279
+ ): Iterable<FieldDiscrepancy> {
340
280
  /**
341
281
  * Similar to the logic used for tracking discrepancies between two node schemas, we will identify
342
282
  * three types of differences:
@@ -348,47 +288,68 @@ function trackObjectNodeDiscrepancies(
348
288
  * Then, the stored schema is iterated to find the third type.
349
289
  */
350
290
 
351
- for (const [fieldKey, fieldStoredSchema] of view.objectNodeFields) {
352
- viewFieldKeys.add(fieldKey);
353
- if (
354
- !stored.objectNodeFields.has(fieldKey) &&
355
- fieldStoredSchema.kind !== storedEmptyFieldSchema.kind
356
- ) {
357
- differences.push({
358
- identifier: fieldKey,
359
- mismatch: "fieldKind",
360
- view: fieldStoredSchema.kind,
361
- stored: storedEmptyFieldSchema.kind,
362
- } satisfies FieldKindIncompatibility);
363
- } else {
364
- differences.push(
365
- ...trackFieldDiscrepancies(
366
- view.objectNodeFields.get(fieldKey) as TreeFieldStoredSchema,
367
- stored.objectNodeFields.get(fieldKey) as TreeFieldStoredSchema,
368
- fieldKey,
369
- ),
370
- );
291
+ for (const result of compareMaps(view.objectNodeFields, stored.objectNodeFields)) {
292
+ const fieldKey = result.key;
293
+ switch (result.type) {
294
+ case "aExtra": {
295
+ if (result.value.kind === storedEmptyFieldSchema.kind) {
296
+ // In one of view/stored, this field is explicitly forbidden, but in the other it is implicitly forbidden
297
+ // (by way of omission). We treat these identically anyway.
298
+ break;
299
+ }
300
+ yield {
301
+ identifier: fieldKey,
302
+ mismatch: "fieldKind",
303
+ view: result.value.kind,
304
+ stored: storedEmptyFieldSchema.kind,
305
+ } satisfies FieldKindDiscrepancy;
306
+ break;
307
+ }
308
+ case "bExtra": {
309
+ if (result.value.kind === storedEmptyFieldSchema.kind) {
310
+ // In one of view/stored, this field is explicitly forbidden, but in the other it is implicitly forbidden
311
+ // (by way of omission). We treat these identically anyway.
312
+ break;
313
+ }
314
+ yield {
315
+ identifier: fieldKey,
316
+ mismatch: "fieldKind",
317
+ view: storedEmptyFieldSchema.kind,
318
+ stored: result.value.kind,
319
+ } satisfies FieldKindDiscrepancy;
320
+ break;
321
+ }
322
+ case "both": {
323
+ yield* getFieldDiscrepancies(result.valueA, result.valueB, fieldKey);
324
+ break;
325
+ }
326
+ default: {
327
+ break;
328
+ }
371
329
  }
372
330
  }
331
+ }
373
332
 
374
- for (const [fieldKey, fieldStoredSchema] of stored.objectNodeFields) {
375
- if (viewFieldKeys.has(fieldKey)) {
376
- continue;
377
- }
378
-
379
- if (fieldStoredSchema.kind !== storedEmptyFieldSchema.kind) {
380
- differences.push({
381
- identifier: fieldKey,
382
- mismatch: "fieldKind",
383
- view: storedEmptyFieldSchema.kind,
384
- stored: fieldStoredSchema.kind,
385
- } satisfies FieldKindIncompatibility);
333
+ function* compareMaps<K, V1, V2>(
334
+ a: ReadonlyMap<K, V1>,
335
+ b: ReadonlyMap<K, V2>,
336
+ ): Iterable<
337
+ | { type: "aExtra"; key: K; value: V1 }
338
+ | { type: "bExtra"; key: K; value: V2 }
339
+ | { type: "both"; key: K; valueA: V1; valueB: V2 }
340
+ > {
341
+ for (const [key, valueA] of a) {
342
+ const valueB = b.get(key);
343
+ yield valueB === undefined
344
+ ? { type: "aExtra", key, value: valueA }
345
+ : { type: "both", key, valueA, valueB };
346
+ }
347
+ for (const [key, valueB] of b) {
348
+ if (!a.has(key)) {
349
+ yield { type: "bExtra", key, value: valueB };
386
350
  }
387
351
  }
388
-
389
- return differences;
390
352
  }
391
-
392
353
  /**
393
354
  * @remarks
394
355
  *
@@ -407,12 +368,12 @@ function trackObjectNodeDiscrepancies(
407
368
  * validating internal fields.
408
369
  */
409
370
  export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema): boolean {
410
- const incompatibilities = getAllowedContentIncompatibilities(view, stored);
371
+ const discrepancies = getAllowedContentDiscrepancies(view, stored);
411
372
 
412
- for (const incompatibility of incompatibilities) {
413
- switch (incompatibility.mismatch) {
373
+ for (const discrepancy of discrepancies) {
374
+ switch (discrepancy.mismatch) {
414
375
  case "nodeKind": {
415
- if (incompatibility.stored !== undefined) {
376
+ if (discrepancy.stored !== undefined) {
416
377
  // It's fine for the view schema to know of a node type that the stored schema doesn't know about.
417
378
  return false;
418
379
  }
@@ -421,15 +382,15 @@ export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema)
421
382
  case "valueSchema":
422
383
  case "allowedTypes":
423
384
  case "fieldKind": {
424
- if (!validateFieldIncompatibility(incompatibility)) {
385
+ if (!isFieldDiscrepancyCompatible(discrepancy)) {
425
386
  return false;
426
387
  }
427
388
  break;
428
389
  }
429
390
  case "fields": {
430
391
  if (
431
- incompatibility.differences.some(
432
- (difference) => !validateFieldIncompatibility(difference),
392
+ discrepancy.differences.some(
393
+ (difference) => !isFieldDiscrepancyCompatible(difference),
433
394
  )
434
395
  ) {
435
396
  return false;
@@ -442,16 +403,16 @@ export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema)
442
403
  return true;
443
404
  }
444
405
 
445
- function validateFieldIncompatibility(incompatibility: FieldIncompatibility): boolean {
446
- switch (incompatibility.mismatch) {
406
+ function isFieldDiscrepancyCompatible(discrepancy: FieldDiscrepancy): boolean {
407
+ switch (discrepancy.mismatch) {
447
408
  case "allowedTypes": {
448
409
  // Since we only track the symmetric difference between the allowed types in the view and
449
410
  // stored schemas, it's sufficient to check if any extra allowed types still exist in the
450
411
  // stored schema.
451
- return incompatibility.stored.length === 0;
412
+ return discrepancy.stored.length === 0;
452
413
  }
453
414
  case "fieldKind": {
454
- return posetLte(incompatibility.stored, incompatibility.view, fieldRealizer);
415
+ return posetLte(discrepancy.stored, discrepancy.view, fieldRealizer);
455
416
  }
456
417
  case "valueSchema": {
457
418
  return false;
@@ -74,4 +74,7 @@ export type {
74
74
  FieldKindConfiguration,
75
75
  FieldKindConfigurationEntry,
76
76
  } from "./fieldKindConfiguration.js";
77
- export { getAllowedContentIncompatibilities, isRepoSuperset } from "./discrepancies.js";
77
+ export {
78
+ getAllowedContentDiscrepancies,
79
+ isRepoSuperset,
80
+ } from "./discrepancies.js";
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/tree";
9
- export const pkgVersion = "2.10.0-306579";
9
+ export const pkgVersion = "2.10.0-307399";
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
6
+ import { unreachableCase } from "@fluidframework/core-utils/internal";
7
7
  import { UsageError } from "@fluidframework/telemetry-utils/internal";
8
8
 
9
9
  import { TreeStatus } from "../feature-libraries/index.js";
@@ -464,10 +464,12 @@ function runTransactionInCheckout<TResult>(
464
464
  switch (constraint.type) {
465
465
  case "nodeInDocument": {
466
466
  const node = getOrCreateInnerNode(constraint.node);
467
- assert(
468
- treeApi.status(constraint.node) === TreeStatus.InDocument,
469
- 0x90f /* Attempted to apply "nodeExists" constraint when building a transaction, but the node is not in the document. */,
470
- );
467
+ const nodeStatus = treeApi.status(constraint.node);
468
+ if (nodeStatus !== TreeStatus.InDocument) {
469
+ throw new UsageError(
470
+ `Attempted to add a "nodeInDocument" constraint, but the node is not currently in the document. Node status: ${nodeStatus}`,
471
+ );
472
+ }
471
473
  checkout.editor.addNodeExistsConstraint(node.anchorNode);
472
474
  break;
473
475
  }