@fluidframework/tree 2.10.0-307060 → 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.
@@ -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,
@@ -107,6 +107,17 @@ export interface NodeFieldsDiscrepancy {
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
122
  * Finds and reports discrepancies between a view schema and a stored schema.
112
123
  *
@@ -124,165 +135,102 @@ type SchemaFactoryNodeKind = "object" | "leaf" | "map";
124
135
  *
125
136
  * @returns the discrepancies between two TreeStoredSchema objects
126
137
  */
127
- export function getAllowedContentDiscrepancies(
138
+ export function* getAllowedContentDiscrepancies(
128
139
  view: TreeStoredSchema,
129
140
  stored: TreeStoredSchema,
130
- ): Discrepancy[] {
131
- const discrepancies: Discrepancy[] = [];
132
-
141
+ ): Iterable<Discrepancy> {
133
142
  // check root schema discrepancies
134
- discrepancies.push(...trackFieldDiscrepancies(view.rootFieldSchema, stored.rootFieldSchema));
135
-
136
- // Verify the existence and type of a node schema given its identifier (key), then determine if
137
- // an exhaustive search is necessary.
138
- const viewNodeKeys = new Set<TreeNodeSchemaIdentifier>();
139
- for (const [key, viewNodeSchema] of view.nodeSchema) {
140
- viewNodeKeys.add(key);
141
-
142
- if (viewNodeSchema instanceof ObjectNodeStoredSchema) {
143
- if (!stored.nodeSchema.has(key)) {
144
- discrepancies.push({
145
- 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,
146
151
  mismatch: "nodeKind",
147
- view: "object",
152
+ view: viewType,
148
153
  stored: undefined,
149
- });
150
- } else {
151
- const storedNodeSchema = stored.nodeSchema.get(key);
152
- assert(
153
- storedNodeSchema !== undefined,
154
- 0x9be /* The storedNodeSchema in stored.nodeSchema should not be undefined */,
155
- );
156
- if (storedNodeSchema instanceof MapNodeStoredSchema) {
157
- discrepancies.push({
158
- identifier: key,
159
- mismatch: "nodeKind",
160
- view: "object",
161
- stored: "map",
162
- } satisfies NodeKindDiscrepancy);
163
- } else if (storedNodeSchema instanceof LeafNodeStoredSchema) {
164
- discrepancies.push({
165
- identifier: key,
166
- mismatch: "nodeKind",
167
- view: "object",
168
- stored: "leaf",
169
- } satisfies NodeKindDiscrepancy);
170
- } else if (storedNodeSchema instanceof ObjectNodeStoredSchema) {
171
- const differences = trackObjectNodeDiscrepancies(viewNodeSchema, storedNodeSchema);
172
- if (differences.length > 0) {
173
- discrepancies.push({
174
- identifier: key,
175
- mismatch: "fields",
176
- differences,
177
- } satisfies NodeFieldsDiscrepancy);
178
- }
179
- } else {
180
- throwUnsupportedNodeType(storedNodeSchema.constructor.name);
181
- }
154
+ };
155
+ break;
182
156
  }
183
- } else if (viewNodeSchema instanceof MapNodeStoredSchema) {
184
- if (!stored.nodeSchema.has(key)) {
185
- discrepancies.push({
186
- identifier: key,
157
+ case "bExtra": {
158
+ const storedType = getNodeSchemaType(result.value);
159
+ yield {
160
+ identifier: result.key,
187
161
  mismatch: "nodeKind",
188
- view: "map",
189
- stored: undefined,
190
- } satisfies NodeKindDiscrepancy);
191
- } else {
192
- const storedNodeSchema = stored.nodeSchema.get(key);
193
- assert(
194
- storedNodeSchema !== undefined,
195
- 0x9bf /* The storedNodeSchema in stored.nodeSchema should not be undefined */,
196
- );
197
- if (storedNodeSchema instanceof ObjectNodeStoredSchema) {
198
- discrepancies.push({
199
- identifier: key,
200
- mismatch: "nodeKind",
201
- view: "map",
202
- stored: "object",
203
- } satisfies NodeKindDiscrepancy);
204
- } else if (storedNodeSchema instanceof LeafNodeStoredSchema) {
205
- discrepancies.push({
206
- identifier: key,
207
- mismatch: "nodeKind",
208
- view: "map",
209
- stored: "leaf",
210
- } satisfies NodeKindDiscrepancy);
211
- } else if (storedNodeSchema instanceof MapNodeStoredSchema) {
212
- discrepancies.push(
213
- ...trackFieldDiscrepancies(
214
- viewNodeSchema.mapFields,
215
- storedNodeSchema.mapFields,
216
- key,
217
- ),
218
- );
219
- } else {
220
- throwUnsupportedNodeType(storedNodeSchema.constructor.name);
221
- }
162
+ view: undefined,
163
+ stored: storedType,
164
+ };
165
+ break;
222
166
  }
223
- } else if (viewNodeSchema instanceof LeafNodeStoredSchema) {
224
- if (!stored.nodeSchema.has(key)) {
225
- discrepancies.push({
226
- identifier: key,
227
- mismatch: "nodeKind",
228
- view: "leaf",
229
- stored: undefined,
230
- });
231
- } else {
232
- const storedNodeSchema = stored.nodeSchema.get(key);
233
- assert(
234
- storedNodeSchema !== undefined,
235
- 0x9c0 /* The storedNodeSchema in stored.nodeSchema should not be undefined */,
236
- );
237
- if (storedNodeSchema instanceof MapNodeStoredSchema) {
238
- discrepancies.push({
239
- identifier: key,
240
- mismatch: "nodeKind",
241
- view: "leaf",
242
- stored: "map",
243
- } satisfies NodeKindDiscrepancy);
244
- } else if (storedNodeSchema instanceof ObjectNodeStoredSchema) {
245
- discrepancies.push({
246
- identifier: key,
247
- mismatch: "nodeKind",
248
- view: "leaf",
249
- stored: "object",
250
- } satisfies NodeKindDiscrepancy);
251
- } else if (storedNodeSchema instanceof LeafNodeStoredSchema) {
252
- if (viewNodeSchema.leafValue !== storedNodeSchema.leafValue) {
253
- discrepancies.push({
254
- identifier: key,
255
- mismatch: "valueSchema",
256
- view: viewNodeSchema.leafValue,
257
- stored: storedNodeSchema.leafValue,
258
- } satisfies ValueSchemaDiscrepancy);
259
- }
260
- } else {
261
- throwUnsupportedNodeType(storedNodeSchema.constructor.name);
262
- }
167
+ case "both": {
168
+ yield* getNodeDiscrepancies(result.key, result.valueA, result.valueB);
169
+ break;
263
170
  }
264
- } else {
265
- throwUnsupportedNodeType(viewNodeSchema.constructor.name);
171
+ default:
172
+ break;
266
173
  }
267
174
  }
175
+ }
268
176
 
269
- for (const [key, storedNodeSchema] of stored.nodeSchema) {
270
- if (!viewNodeKeys.has(key)) {
271
- discrepancies.push({
272
- identifier: key,
273
- mismatch: "nodeKind",
274
- view: undefined,
275
- stored:
276
- storedNodeSchema instanceof MapNodeStoredSchema
277
- ? "map"
278
- : storedNodeSchema instanceof ObjectNodeStoredSchema
279
- ? "object"
280
- : "leaf",
281
- } satisfies NodeKindDiscrepancy);
282
- }
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;
283
192
  }
284
193
 
285
- return discrepancies;
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
+ }
286
234
  }
287
235
 
288
236
  /**
@@ -290,13 +238,11 @@ export function getAllowedContentDiscrepancies(
290
238
  *
291
239
  * @param keyOrRoot - If the key is missing, it indicates that this is the root field schema.
292
240
  */
293
- function trackFieldDiscrepancies(
241
+ function* getFieldDiscrepancies(
294
242
  view: TreeFieldStoredSchema,
295
243
  stored: TreeFieldStoredSchema,
296
244
  keyOrRoot?: string,
297
- ): FieldDiscrepancy[] {
298
- const differences: FieldDiscrepancy[] = [];
299
-
245
+ ): Iterable<FieldDiscrepancy> {
300
246
  // Only track the symmetric differences of two sets.
301
247
  const findSetDiscrepancies = (
302
248
  a: TreeTypeSet,
@@ -307,34 +253,30 @@ function trackFieldDiscrepancies(
307
253
  return [aDiff, bDiff];
308
254
  };
309
255
 
310
- const allowedTypesDiscrepancies = findSetDiscrepancies(view.types, stored.types);
311
- if (allowedTypesDiscrepancies[0].length > 0 || allowedTypesDiscrepancies[1].length > 0) {
312
- differences.push({
256
+ const [viewExtra, storedExtra] = findSetDiscrepancies(view.types, stored.types);
257
+ if (viewExtra.length > 0 || storedExtra.length > 0) {
258
+ yield {
313
259
  identifier: keyOrRoot,
314
260
  mismatch: "allowedTypes",
315
- view: allowedTypesDiscrepancies[0],
316
- stored: allowedTypesDiscrepancies[1],
317
- } satisfies AllowedTypeDiscrepancy);
261
+ view: viewExtra,
262
+ stored: storedExtra,
263
+ } satisfies AllowedTypeDiscrepancy;
318
264
  }
319
265
 
320
266
  if (view.kind !== stored.kind) {
321
- differences.push({
267
+ yield {
322
268
  identifier: keyOrRoot,
323
269
  mismatch: "fieldKind",
324
270
  view: view.kind,
325
271
  stored: stored.kind,
326
- } satisfies FieldKindDiscrepancy);
272
+ } satisfies FieldKindDiscrepancy;
327
273
  }
328
-
329
- return differences;
330
274
  }
331
275
 
332
- function trackObjectNodeDiscrepancies(
276
+ function* trackObjectNodeDiscrepancies(
333
277
  view: ObjectNodeStoredSchema,
334
278
  stored: ObjectNodeStoredSchema,
335
- ): FieldDiscrepancy[] {
336
- const differences: FieldDiscrepancy[] = [];
337
- const viewFieldKeys = new Set<FieldKey>();
279
+ ): Iterable<FieldDiscrepancy> {
338
280
  /**
339
281
  * Similar to the logic used for tracking discrepancies between two node schemas, we will identify
340
282
  * three types of differences:
@@ -346,47 +288,68 @@ function trackObjectNodeDiscrepancies(
346
288
  * Then, the stored schema is iterated to find the third type.
347
289
  */
348
290
 
349
- for (const [fieldKey, fieldStoredSchema] of view.objectNodeFields) {
350
- viewFieldKeys.add(fieldKey);
351
- if (
352
- !stored.objectNodeFields.has(fieldKey) &&
353
- fieldStoredSchema.kind !== storedEmptyFieldSchema.kind
354
- ) {
355
- differences.push({
356
- identifier: fieldKey,
357
- mismatch: "fieldKind",
358
- view: fieldStoredSchema.kind,
359
- stored: storedEmptyFieldSchema.kind,
360
- } satisfies FieldKindDiscrepancy);
361
- } else {
362
- differences.push(
363
- ...trackFieldDiscrepancies(
364
- view.objectNodeFields.get(fieldKey) as TreeFieldStoredSchema,
365
- stored.objectNodeFields.get(fieldKey) as TreeFieldStoredSchema,
366
- fieldKey,
367
- ),
368
- );
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
+ }
369
329
  }
370
330
  }
331
+ }
371
332
 
372
- for (const [fieldKey, fieldStoredSchema] of stored.objectNodeFields) {
373
- if (viewFieldKeys.has(fieldKey)) {
374
- continue;
375
- }
376
-
377
- if (fieldStoredSchema.kind !== storedEmptyFieldSchema.kind) {
378
- differences.push({
379
- identifier: fieldKey,
380
- mismatch: "fieldKind",
381
- view: storedEmptyFieldSchema.kind,
382
- stored: fieldStoredSchema.kind,
383
- } satisfies FieldKindDiscrepancy);
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 };
384
350
  }
385
351
  }
386
-
387
- return differences;
388
352
  }
389
-
390
353
  /**
391
354
  * @remarks
392
355
  *
@@ -419,7 +382,7 @@ export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema)
419
382
  case "valueSchema":
420
383
  case "allowedTypes":
421
384
  case "fieldKind": {
422
- if (!validateFieldIncompatibility(discrepancy)) {
385
+ if (!isFieldDiscrepancyCompatible(discrepancy)) {
423
386
  return false;
424
387
  }
425
388
  break;
@@ -427,7 +390,7 @@ export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema)
427
390
  case "fields": {
428
391
  if (
429
392
  discrepancy.differences.some(
430
- (difference) => !validateFieldIncompatibility(difference),
393
+ (difference) => !isFieldDiscrepancyCompatible(difference),
431
394
  )
432
395
  ) {
433
396
  return false;
@@ -440,7 +403,7 @@ export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema)
440
403
  return true;
441
404
  }
442
405
 
443
- function validateFieldIncompatibility(discrepancy: FieldDiscrepancy): boolean {
406
+ function isFieldDiscrepancyCompatible(discrepancy: FieldDiscrepancy): boolean {
444
407
  switch (discrepancy.mismatch) {
445
408
  case "allowedTypes": {
446
409
  // Since we only track the symmetric difference between the allowed types in the view and
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/tree";
9
- export const pkgVersion = "2.10.0-307060";
9
+ export const pkgVersion = "2.10.0-307399";