@fluidframework/tree 2.10.0-306579 → 2.10.0-307060

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 +33 -33
  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 +31 -31
  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 +20 -20
  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 +75 -77
  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
@@ -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,62 +78,60 @@ 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
110
  /**
111
- * @remarks
111
+ * Finds and reports discrepancies between a view schema and a stored schema.
112
112
  *
113
113
  * The workflow for finding schema incompatibilities:
114
- * 1. Compare the two root schemas to identify any `FieldIncompatibility`.
114
+ * 1. Compare the two root schemas to identify any `FieldDiscrepancy`.
115
115
  *
116
116
  * 2. For each node schema in the `view`:
117
117
  * - 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`
118
+ * consistent. Otherwise this difference is treated as `NodeKindDiscrepancy`
119
119
  * - 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`.
120
+ * are consistent, perform a exhaustive validation to identify all `FieldDiscrepancy`.
121
121
  *
122
122
  * 3. For each node schema in the stored, verify if it exists in the view. The overlapping parts were already
123
123
  * addressed in the previous step.
124
124
  *
125
125
  * @returns the discrepancies between two TreeStoredSchema objects
126
126
  */
127
- export function getAllowedContentIncompatibilities(
127
+ export function getAllowedContentDiscrepancies(
128
128
  view: TreeStoredSchema,
129
129
  stored: TreeStoredSchema,
130
- ): Incompatibility[] {
131
- const incompatibilities: Incompatibility[] = [];
130
+ ): Discrepancy[] {
131
+ const discrepancies: Discrepancy[] = [];
132
132
 
133
133
  // check root schema discrepancies
134
- incompatibilities.push(
135
- ...trackFieldDiscrepancies(view.rootFieldSchema, stored.rootFieldSchema),
136
- );
134
+ discrepancies.push(...trackFieldDiscrepancies(view.rootFieldSchema, stored.rootFieldSchema));
137
135
 
138
136
  // Verify the existence and type of a node schema given its identifier (key), then determine if
139
137
  // an exhaustive search is necessary.
@@ -143,7 +141,7 @@ export function getAllowedContentIncompatibilities(
143
141
 
144
142
  if (viewNodeSchema instanceof ObjectNodeStoredSchema) {
145
143
  if (!stored.nodeSchema.has(key)) {
146
- incompatibilities.push({
144
+ discrepancies.push({
147
145
  identifier: key,
148
146
  mismatch: "nodeKind",
149
147
  view: "object",
@@ -156,27 +154,27 @@ export function getAllowedContentIncompatibilities(
156
154
  0x9be /* The storedNodeSchema in stored.nodeSchema should not be undefined */,
157
155
  );
158
156
  if (storedNodeSchema instanceof MapNodeStoredSchema) {
159
- incompatibilities.push({
157
+ discrepancies.push({
160
158
  identifier: key,
161
159
  mismatch: "nodeKind",
162
160
  view: "object",
163
161
  stored: "map",
164
- } satisfies NodeKindIncompatibility);
162
+ } satisfies NodeKindDiscrepancy);
165
163
  } else if (storedNodeSchema instanceof LeafNodeStoredSchema) {
166
- incompatibilities.push({
164
+ discrepancies.push({
167
165
  identifier: key,
168
166
  mismatch: "nodeKind",
169
167
  view: "object",
170
168
  stored: "leaf",
171
- } satisfies NodeKindIncompatibility);
169
+ } satisfies NodeKindDiscrepancy);
172
170
  } else if (storedNodeSchema instanceof ObjectNodeStoredSchema) {
173
171
  const differences = trackObjectNodeDiscrepancies(viewNodeSchema, storedNodeSchema);
174
172
  if (differences.length > 0) {
175
- incompatibilities.push({
173
+ discrepancies.push({
176
174
  identifier: key,
177
175
  mismatch: "fields",
178
176
  differences,
179
- } satisfies NodeFieldsIncompatibility);
177
+ } satisfies NodeFieldsDiscrepancy);
180
178
  }
181
179
  } else {
182
180
  throwUnsupportedNodeType(storedNodeSchema.constructor.name);
@@ -184,12 +182,12 @@ export function getAllowedContentIncompatibilities(
184
182
  }
185
183
  } else if (viewNodeSchema instanceof MapNodeStoredSchema) {
186
184
  if (!stored.nodeSchema.has(key)) {
187
- incompatibilities.push({
185
+ discrepancies.push({
188
186
  identifier: key,
189
187
  mismatch: "nodeKind",
190
188
  view: "map",
191
189
  stored: undefined,
192
- } satisfies NodeKindIncompatibility);
190
+ } satisfies NodeKindDiscrepancy);
193
191
  } else {
194
192
  const storedNodeSchema = stored.nodeSchema.get(key);
195
193
  assert(
@@ -197,21 +195,21 @@ export function getAllowedContentIncompatibilities(
197
195
  0x9bf /* The storedNodeSchema in stored.nodeSchema should not be undefined */,
198
196
  );
199
197
  if (storedNodeSchema instanceof ObjectNodeStoredSchema) {
200
- incompatibilities.push({
198
+ discrepancies.push({
201
199
  identifier: key,
202
200
  mismatch: "nodeKind",
203
201
  view: "map",
204
202
  stored: "object",
205
- } satisfies NodeKindIncompatibility);
203
+ } satisfies NodeKindDiscrepancy);
206
204
  } else if (storedNodeSchema instanceof LeafNodeStoredSchema) {
207
- incompatibilities.push({
205
+ discrepancies.push({
208
206
  identifier: key,
209
207
  mismatch: "nodeKind",
210
208
  view: "map",
211
209
  stored: "leaf",
212
- } satisfies NodeKindIncompatibility);
210
+ } satisfies NodeKindDiscrepancy);
213
211
  } else if (storedNodeSchema instanceof MapNodeStoredSchema) {
214
- incompatibilities.push(
212
+ discrepancies.push(
215
213
  ...trackFieldDiscrepancies(
216
214
  viewNodeSchema.mapFields,
217
215
  storedNodeSchema.mapFields,
@@ -224,7 +222,7 @@ export function getAllowedContentIncompatibilities(
224
222
  }
225
223
  } else if (viewNodeSchema instanceof LeafNodeStoredSchema) {
226
224
  if (!stored.nodeSchema.has(key)) {
227
- incompatibilities.push({
225
+ discrepancies.push({
228
226
  identifier: key,
229
227
  mismatch: "nodeKind",
230
228
  view: "leaf",
@@ -237,27 +235,27 @@ export function getAllowedContentIncompatibilities(
237
235
  0x9c0 /* The storedNodeSchema in stored.nodeSchema should not be undefined */,
238
236
  );
239
237
  if (storedNodeSchema instanceof MapNodeStoredSchema) {
240
- incompatibilities.push({
238
+ discrepancies.push({
241
239
  identifier: key,
242
240
  mismatch: "nodeKind",
243
241
  view: "leaf",
244
242
  stored: "map",
245
- } satisfies NodeKindIncompatibility);
243
+ } satisfies NodeKindDiscrepancy);
246
244
  } else if (storedNodeSchema instanceof ObjectNodeStoredSchema) {
247
- incompatibilities.push({
245
+ discrepancies.push({
248
246
  identifier: key,
249
247
  mismatch: "nodeKind",
250
248
  view: "leaf",
251
249
  stored: "object",
252
- } satisfies NodeKindIncompatibility);
250
+ } satisfies NodeKindDiscrepancy);
253
251
  } else if (storedNodeSchema instanceof LeafNodeStoredSchema) {
254
252
  if (viewNodeSchema.leafValue !== storedNodeSchema.leafValue) {
255
- incompatibilities.push({
253
+ discrepancies.push({
256
254
  identifier: key,
257
255
  mismatch: "valueSchema",
258
256
  view: viewNodeSchema.leafValue,
259
257
  stored: storedNodeSchema.leafValue,
260
- } satisfies ValueSchemaIncompatibility);
258
+ } satisfies ValueSchemaDiscrepancy);
261
259
  }
262
260
  } else {
263
261
  throwUnsupportedNodeType(storedNodeSchema.constructor.name);
@@ -270,7 +268,7 @@ export function getAllowedContentIncompatibilities(
270
268
 
271
269
  for (const [key, storedNodeSchema] of stored.nodeSchema) {
272
270
  if (!viewNodeKeys.has(key)) {
273
- incompatibilities.push({
271
+ discrepancies.push({
274
272
  identifier: key,
275
273
  mismatch: "nodeKind",
276
274
  view: undefined,
@@ -280,11 +278,11 @@ export function getAllowedContentIncompatibilities(
280
278
  : storedNodeSchema instanceof ObjectNodeStoredSchema
281
279
  ? "object"
282
280
  : "leaf",
283
- } satisfies NodeKindIncompatibility);
281
+ } satisfies NodeKindDiscrepancy);
284
282
  }
285
283
  }
286
284
 
287
- return incompatibilities;
285
+ return discrepancies;
288
286
  }
289
287
 
290
288
  /**
@@ -296,8 +294,8 @@ function trackFieldDiscrepancies(
296
294
  view: TreeFieldStoredSchema,
297
295
  stored: TreeFieldStoredSchema,
298
296
  keyOrRoot?: string,
299
- ): FieldIncompatibility[] {
300
- const differences: FieldIncompatibility[] = [];
297
+ ): FieldDiscrepancy[] {
298
+ const differences: FieldDiscrepancy[] = [];
301
299
 
302
300
  // Only track the symmetric differences of two sets.
303
301
  const findSetDiscrepancies = (
@@ -316,7 +314,7 @@ function trackFieldDiscrepancies(
316
314
  mismatch: "allowedTypes",
317
315
  view: allowedTypesDiscrepancies[0],
318
316
  stored: allowedTypesDiscrepancies[1],
319
- } satisfies AllowedTypeIncompatibility);
317
+ } satisfies AllowedTypeDiscrepancy);
320
318
  }
321
319
 
322
320
  if (view.kind !== stored.kind) {
@@ -325,7 +323,7 @@ function trackFieldDiscrepancies(
325
323
  mismatch: "fieldKind",
326
324
  view: view.kind,
327
325
  stored: stored.kind,
328
- } satisfies FieldKindIncompatibility);
326
+ } satisfies FieldKindDiscrepancy);
329
327
  }
330
328
 
331
329
  return differences;
@@ -334,8 +332,8 @@ function trackFieldDiscrepancies(
334
332
  function trackObjectNodeDiscrepancies(
335
333
  view: ObjectNodeStoredSchema,
336
334
  stored: ObjectNodeStoredSchema,
337
- ): FieldIncompatibility[] {
338
- const differences: FieldIncompatibility[] = [];
335
+ ): FieldDiscrepancy[] {
336
+ const differences: FieldDiscrepancy[] = [];
339
337
  const viewFieldKeys = new Set<FieldKey>();
340
338
  /**
341
339
  * Similar to the logic used for tracking discrepancies between two node schemas, we will identify
@@ -359,7 +357,7 @@ function trackObjectNodeDiscrepancies(
359
357
  mismatch: "fieldKind",
360
358
  view: fieldStoredSchema.kind,
361
359
  stored: storedEmptyFieldSchema.kind,
362
- } satisfies FieldKindIncompatibility);
360
+ } satisfies FieldKindDiscrepancy);
363
361
  } else {
364
362
  differences.push(
365
363
  ...trackFieldDiscrepancies(
@@ -382,7 +380,7 @@ function trackObjectNodeDiscrepancies(
382
380
  mismatch: "fieldKind",
383
381
  view: storedEmptyFieldSchema.kind,
384
382
  stored: fieldStoredSchema.kind,
385
- } satisfies FieldKindIncompatibility);
383
+ } satisfies FieldKindDiscrepancy);
386
384
  }
387
385
  }
388
386
 
@@ -407,12 +405,12 @@ function trackObjectNodeDiscrepancies(
407
405
  * validating internal fields.
408
406
  */
409
407
  export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema): boolean {
410
- const incompatibilities = getAllowedContentIncompatibilities(view, stored);
408
+ const discrepancies = getAllowedContentDiscrepancies(view, stored);
411
409
 
412
- for (const incompatibility of incompatibilities) {
413
- switch (incompatibility.mismatch) {
410
+ for (const discrepancy of discrepancies) {
411
+ switch (discrepancy.mismatch) {
414
412
  case "nodeKind": {
415
- if (incompatibility.stored !== undefined) {
413
+ if (discrepancy.stored !== undefined) {
416
414
  // It's fine for the view schema to know of a node type that the stored schema doesn't know about.
417
415
  return false;
418
416
  }
@@ -421,14 +419,14 @@ export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema)
421
419
  case "valueSchema":
422
420
  case "allowedTypes":
423
421
  case "fieldKind": {
424
- if (!validateFieldIncompatibility(incompatibility)) {
422
+ if (!validateFieldIncompatibility(discrepancy)) {
425
423
  return false;
426
424
  }
427
425
  break;
428
426
  }
429
427
  case "fields": {
430
428
  if (
431
- incompatibility.differences.some(
429
+ discrepancy.differences.some(
432
430
  (difference) => !validateFieldIncompatibility(difference),
433
431
  )
434
432
  ) {
@@ -442,16 +440,16 @@ export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema)
442
440
  return true;
443
441
  }
444
442
 
445
- function validateFieldIncompatibility(incompatibility: FieldIncompatibility): boolean {
446
- switch (incompatibility.mismatch) {
443
+ function validateFieldIncompatibility(discrepancy: FieldDiscrepancy): boolean {
444
+ switch (discrepancy.mismatch) {
447
445
  case "allowedTypes": {
448
446
  // Since we only track the symmetric difference between the allowed types in the view and
449
447
  // stored schemas, it's sufficient to check if any extra allowed types still exist in the
450
448
  // stored schema.
451
- return incompatibility.stored.length === 0;
449
+ return discrepancy.stored.length === 0;
452
450
  }
453
451
  case "fieldKind": {
454
- return posetLte(incompatibility.stored, incompatibility.view, fieldRealizer);
452
+ return posetLte(discrepancy.stored, discrepancy.view, fieldRealizer);
455
453
  }
456
454
  case "valueSchema": {
457
455
  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-307060";
@@ -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
  }
@@ -73,8 +73,8 @@ export function singletonSchema<TScope extends string, TName extends string | nu
73
73
  /**
74
74
  * Converts an enum into a collection of schema which can be used in a union.
75
75
  * @remarks
76
- * Currently only supports `string` enums.
77
76
  * The string value of the enum is used as the name of the schema: callers must ensure that it is stable and unique.
77
+ * Numeric enums values have the value implicitly converted into a string.
78
78
  * Consider making a dedicated schema factory with a nested scope to avoid the enum members colliding with other schema.
79
79
  * @example
80
80
  * ```typescript
@@ -87,7 +87,7 @@ export function singletonSchema<TScope extends string, TName extends string | nu
87
87
  * // Define the schema for each member of the enum using a nested scope to group them together.
88
88
  * const ModeNodes = adaptEnum(new SchemaFactory(`${schemaFactory.scope}.Mode`), Mode);
89
89
  * // Defined the types of the nodes which correspond to this the schema.
90
- * type ModeNodes = NodeFromSchema<(typeof ModeNodes.schema)[number]>;
90
+ * type ModeNodes = TreeNodeFromImplicitAllowedTypes<(typeof ModeNodes.schema)>;
91
91
  * // An example schema which has an enum as a child.
92
92
  * class Parent extends schemaFactory.object("Parent", {
93
93
  * // adaptEnum's return value has a ".schema" property can be use as an `AllowedTypes` array allowing any of the members of the enum.
@@ -106,8 +106,6 @@ export function singletonSchema<TScope extends string, TName extends string | nu
106
106
  * }
107
107
  * ```
108
108
  * @privateRemarks
109
- * TODO:
110
- * Extend this to support numeric enums.
111
109
  * Maybe provide `SchemaFactory.nested` to ease creating nested scopes?
112
110
  * @see {@link enumFromStrings} for a similar function that works on arrays of strings instead of an enum.
113
111
  * @alpha
@@ -139,9 +137,12 @@ export function adaptEnum<
139
137
 
140
138
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
141
139
  const factoryOut = <TValue extends Values>(value: TValue) => {
142
- return new out[inverse.get(value) ?? fail("missing enum value")]() as NodeFromSchema<
143
- ReturnType<typeof singletonSchema<TScope, TValue>>
144
- >;
140
+ return new out[
141
+ inverse.get(value) ?? fail("missing enum value")
142
+ // "extends unknown" is required here to handle when TValue is an union: each member of the union should be processed independently.
143
+ ]() as TValue extends unknown
144
+ ? NodeFromSchema<ReturnType<typeof singletonSchema<TScope, TValue>>>
145
+ : never;
145
146
  };
146
147
  const out = factoryOut as typeof factoryOut & TOut & { readonly schema: SchemaArray };
147
148
  for (const [key, value] of Object.entries(members)) {
@@ -176,7 +177,7 @@ export function adaptEnum<
176
177
  * ```typescript
177
178
  * const schemaFactory = new SchemaFactory("com.myApp");
178
179
  * const Mode = enumFromStrings(schemaFactory, ["Fun", "Cool"]);
179
- * type Mode = NodeFromSchema<(typeof Mode.schema)[number]>;
180
+ * type Mode = TreeNodeFromImplicitAllowedTypes<typeof Mode.schema>;
180
181
  * const nodeFromString: Mode = Mode("Fun");
181
182
  * const nodeFromSchema: Mode = new Mode.Fun();
182
183
  *
@@ -198,21 +199,32 @@ export function enumFromStrings<
198
199
  throw new UsageError("All members of enums must have distinct names");
199
200
  }
200
201
 
201
- type TOut = Record<
202
- Members[number],
203
- ReturnType<typeof singletonSchema<TScope, Members[number]>>
204
- >;
205
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
206
- const factoryOut = <TValue extends Members[number]>(value: TValue) => {
207
- return new out[value]() as NodeFromSchema<
208
- ReturnType<typeof singletonSchema<TScope, TValue>>
202
+ type MembersUnion = Members[number];
203
+
204
+ // Get all keys of the Members tuple which are numeric strings as union of numbers:
205
+ type Indexes = Extract<keyof Members, `${number}`> extends `${infer N extends number}`
206
+ ? N
207
+ : never;
208
+
209
+ type TOut = {
210
+ [Index in Indexes as Members[Index]]: ReturnType<
211
+ typeof singletonSchema<TScope, Members[Index] & string>
209
212
  >;
210
213
  };
211
214
 
212
- type SchemaArray = UnionToTuple<TOut[Members[number]]>;
215
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
216
+ const factoryOut = <TValue extends MembersUnion>(value: TValue) => {
217
+ // "extends unknown" is required here to handle when TValue is an union: each member of the union should be processed independently.
218
+ return new recordOut[value]() as TValue extends unknown
219
+ ? NodeFromSchema<ReturnType<typeof singletonSchema<TScope, TValue>>>
220
+ : never;
221
+ };
222
+
223
+ type SchemaArray = UnionToTuple<MembersUnion extends unknown ? TOut[MembersUnion] : never>;
213
224
  const schemaArray: TreeNodeSchema[] = [];
214
225
 
215
226
  const out = factoryOut as typeof factoryOut & TOut & { readonly schema: SchemaArray };
227
+ const recordOut = out as Record<MembersUnion, new () => unknown>;
216
228
  for (const name of members) {
217
229
  const schema = singletonSchema(factory, name);
218
230
  schemaArray.push(schema);
@@ -804,6 +804,8 @@ export class SchemaFactory<
804
804
  name,
805
805
  allowedTypes as T & ImplicitAllowedTypes,
806
806
  true,
807
+ // Setting this (implicitlyConstructable) to true seems to work ok currently, but not for other node kinds.
808
+ // Supporting this could be fragile and might break other future changes, so it's being kept as false for now.
807
809
  false,
808
810
  );
809
811
 
@@ -811,24 +813,29 @@ export class SchemaFactory<
811
813
  ScopedSchemaName<TScope, Name>,
812
814
  NodeKind.Map,
813
815
  TreeMapNodeUnsafe<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Map>,
814
- {
815
- /**
816
- * Iterator for the iterable of content for this node.
817
- * @privateRemarks
818
- * Wrapping the constructor parameter for recursive arrays and maps in an inlined object type avoids (for unknown reasons)
819
- * the following compile error when declaring the recursive schema:
820
- * `Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.`
821
- * To benefit from this without impacting the API, the definition of `Iterable` has been inlined as such an object.
822
- *
823
- * If this workaround is kept, ideally this comment would be deduplicated with the other instance of it.
824
- * Unfortunately attempts to do this failed to avoid the compile error this was introduced to solve.
825
- */
826
- [Symbol.iterator](): Iterator<
827
- [string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>]
828
- >;
829
- },
830
- // Ideally this would be included, but doing so breaks recursive types.
831
- // | RestrictiveStringRecord<InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>>,
816
+ | {
817
+ /**
818
+ * Iterator for the iterable of content for this node.
819
+ * @privateRemarks
820
+ * Wrapping the constructor parameter for recursive arrays and maps in an inlined object type avoids (for unknown reasons)
821
+ * the following compile error when declaring the recursive schema:
822
+ * `Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.`
823
+ * To benefit from this without impacting the API, the definition of `Iterable` has been inlined as such an object.
824
+ *
825
+ * If this workaround is kept, ideally this comment would be deduplicated with the other instance of it.
826
+ * Unfortunately attempts to do this failed to avoid the compile error this was introduced to solve.
827
+ */
828
+ [Symbol.iterator](): Iterator<
829
+ [string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>]
830
+ >;
831
+ }
832
+ // Ideally this would be
833
+ // RestrictiveStringRecord<InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>>,
834
+ // but doing so breaks recursive types.
835
+ // Instead we do a less nice version:
836
+ | {
837
+ readonly [P in string]: InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>;
838
+ },
832
839
  false,
833
840
  T,
834
841
  undefined
@@ -140,7 +140,7 @@ export type ValidateRecursiveSchema<
140
140
  ? Iterable<[string, InsertableTreeNodeFromImplicitAllowedTypes<T["info"]>]>
141
141
  : unknown;
142
142
  }[T["kind"]],
143
- // ImplicitlyConstructable: recursive types are not implicitly constructable.
143
+ // ImplicitlyConstructable: recursive types are currently not implicitly constructable.
144
144
  false,
145
145
  // Info: What's passed to the method to create the schema. Constraining these here should be about as effective as if the actual constraints existed on the actual method itself.
146
146
  {