@fluidframework/tree 2.3.0 → 2.4.0-294316

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 (152) hide show
  1. package/api-report/tree.alpha.api.md +21 -12
  2. package/api-report/tree.beta.api.md +14 -5
  3. package/api-report/tree.legacy.alpha.api.md +14 -5
  4. package/api-report/tree.legacy.public.api.md +14 -5
  5. package/api-report/tree.public.api.md +14 -5
  6. package/dist/alpha.d.ts +1 -0
  7. package/dist/beta.d.ts +1 -0
  8. package/dist/core/index.d.ts +1 -1
  9. package/dist/core/index.d.ts.map +1 -1
  10. package/dist/core/index.js +2 -1
  11. package/dist/core/index.js.map +1 -1
  12. package/dist/core/rebase/index.d.ts +1 -1
  13. package/dist/core/rebase/index.d.ts.map +1 -1
  14. package/dist/core/rebase/index.js +2 -1
  15. package/dist/core/rebase/index.js.map +1 -1
  16. package/dist/core/rebase/types.d.ts +1 -0
  17. package/dist/core/rebase/types.d.ts.map +1 -1
  18. package/dist/core/rebase/types.js +8 -1
  19. package/dist/core/rebase/types.js.map +1 -1
  20. package/dist/core/schema-stored/schema.d.ts.map +1 -1
  21. package/dist/core/schema-stored/schema.js.map +1 -1
  22. package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts +7 -0
  23. package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  24. package/dist/feature-libraries/modular-schema/modularChangeFamily.js +61 -17
  25. package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  26. package/dist/feature-libraries/sequence-field/compose.d.ts.map +1 -1
  27. package/dist/feature-libraries/sequence-field/compose.js +3 -0
  28. package/dist/feature-libraries/sequence-field/compose.js.map +1 -1
  29. package/dist/feature-libraries/sequence-field/utils.d.ts.map +1 -1
  30. package/dist/feature-libraries/sequence-field/utils.js +1 -4
  31. package/dist/feature-libraries/sequence-field/utils.js.map +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/legacy.d.ts +1 -0
  36. package/dist/packageVersion.d.ts +1 -1
  37. package/dist/packageVersion.d.ts.map +1 -1
  38. package/dist/packageVersion.js +1 -1
  39. package/dist/packageVersion.js.map +1 -1
  40. package/dist/public.d.ts +1 -0
  41. package/dist/shared-tree/schematizingTreeView.d.ts +1 -0
  42. package/dist/shared-tree/schematizingTreeView.d.ts.map +1 -1
  43. package/dist/shared-tree/schematizingTreeView.js +3 -0
  44. package/dist/shared-tree/schematizingTreeView.js.map +1 -1
  45. package/dist/simple-tree/api/jsonSchema.d.ts +13 -14
  46. package/dist/simple-tree/api/jsonSchema.d.ts.map +1 -1
  47. package/dist/simple-tree/api/jsonSchema.js.map +1 -1
  48. package/dist/simple-tree/api/schemaFactory.d.ts +8 -2
  49. package/dist/simple-tree/api/schemaFactory.d.ts.map +1 -1
  50. package/dist/simple-tree/api/schemaFactory.js +6 -0
  51. package/dist/simple-tree/api/schemaFactory.js.map +1 -1
  52. package/dist/simple-tree/api/simpleSchema.d.ts +4 -0
  53. package/dist/simple-tree/api/simpleSchema.d.ts.map +1 -1
  54. package/dist/simple-tree/api/simpleSchema.js.map +1 -1
  55. package/dist/simple-tree/api/simpleSchemaToJsonSchema.d.ts.map +1 -1
  56. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js +30 -17
  57. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  58. package/dist/simple-tree/api/tree.d.ts +4 -0
  59. package/dist/simple-tree/api/tree.d.ts.map +1 -1
  60. package/dist/simple-tree/api/tree.js.map +1 -1
  61. package/dist/simple-tree/api/viewSchemaToSimpleSchema.js +4 -0
  62. package/dist/simple-tree/api/viewSchemaToSimpleSchema.js.map +1 -1
  63. package/dist/simple-tree/index.d.ts +1 -1
  64. package/dist/simple-tree/index.d.ts.map +1 -1
  65. package/dist/simple-tree/index.js.map +1 -1
  66. package/dist/simple-tree/schemaTypes.d.ts +44 -4
  67. package/dist/simple-tree/schemaTypes.d.ts.map +1 -1
  68. package/dist/simple-tree/schemaTypes.js +10 -0
  69. package/dist/simple-tree/schemaTypes.js.map +1 -1
  70. package/lib/alpha.d.ts +1 -0
  71. package/lib/beta.d.ts +1 -0
  72. package/lib/core/index.d.ts +1 -1
  73. package/lib/core/index.d.ts.map +1 -1
  74. package/lib/core/index.js +1 -1
  75. package/lib/core/index.js.map +1 -1
  76. package/lib/core/rebase/index.d.ts +1 -1
  77. package/lib/core/rebase/index.d.ts.map +1 -1
  78. package/lib/core/rebase/index.js +1 -1
  79. package/lib/core/rebase/index.js.map +1 -1
  80. package/lib/core/rebase/types.d.ts +1 -0
  81. package/lib/core/rebase/types.d.ts.map +1 -1
  82. package/lib/core/rebase/types.js +6 -0
  83. package/lib/core/rebase/types.js.map +1 -1
  84. package/lib/core/schema-stored/schema.d.ts.map +1 -1
  85. package/lib/core/schema-stored/schema.js.map +1 -1
  86. package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts +7 -0
  87. package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  88. package/lib/feature-libraries/modular-schema/modularChangeFamily.js +62 -18
  89. package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  90. package/lib/feature-libraries/sequence-field/compose.d.ts.map +1 -1
  91. package/lib/feature-libraries/sequence-field/compose.js +3 -0
  92. package/lib/feature-libraries/sequence-field/compose.js.map +1 -1
  93. package/lib/feature-libraries/sequence-field/utils.d.ts.map +1 -1
  94. package/lib/feature-libraries/sequence-field/utils.js +2 -5
  95. package/lib/feature-libraries/sequence-field/utils.js.map +1 -1
  96. package/lib/index.d.ts +1 -1
  97. package/lib/index.d.ts.map +1 -1
  98. package/lib/index.js.map +1 -1
  99. package/lib/legacy.d.ts +1 -0
  100. package/lib/packageVersion.d.ts +1 -1
  101. package/lib/packageVersion.d.ts.map +1 -1
  102. package/lib/packageVersion.js +1 -1
  103. package/lib/packageVersion.js.map +1 -1
  104. package/lib/public.d.ts +1 -0
  105. package/lib/shared-tree/schematizingTreeView.d.ts +1 -0
  106. package/lib/shared-tree/schematizingTreeView.d.ts.map +1 -1
  107. package/lib/shared-tree/schematizingTreeView.js +3 -0
  108. package/lib/shared-tree/schematizingTreeView.js.map +1 -1
  109. package/lib/simple-tree/api/jsonSchema.d.ts +13 -14
  110. package/lib/simple-tree/api/jsonSchema.d.ts.map +1 -1
  111. package/lib/simple-tree/api/jsonSchema.js.map +1 -1
  112. package/lib/simple-tree/api/schemaFactory.d.ts +8 -2
  113. package/lib/simple-tree/api/schemaFactory.d.ts.map +1 -1
  114. package/lib/simple-tree/api/schemaFactory.js +6 -0
  115. package/lib/simple-tree/api/schemaFactory.js.map +1 -1
  116. package/lib/simple-tree/api/simpleSchema.d.ts +4 -0
  117. package/lib/simple-tree/api/simpleSchema.d.ts.map +1 -1
  118. package/lib/simple-tree/api/simpleSchema.js.map +1 -1
  119. package/lib/simple-tree/api/simpleSchemaToJsonSchema.d.ts.map +1 -1
  120. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js +31 -18
  121. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  122. package/lib/simple-tree/api/tree.d.ts +4 -0
  123. package/lib/simple-tree/api/tree.d.ts.map +1 -1
  124. package/lib/simple-tree/api/tree.js.map +1 -1
  125. package/lib/simple-tree/api/viewSchemaToSimpleSchema.js +4 -0
  126. package/lib/simple-tree/api/viewSchemaToSimpleSchema.js.map +1 -1
  127. package/lib/simple-tree/index.d.ts +1 -1
  128. package/lib/simple-tree/index.d.ts.map +1 -1
  129. package/lib/simple-tree/index.js.map +1 -1
  130. package/lib/simple-tree/schemaTypes.d.ts +44 -4
  131. package/lib/simple-tree/schemaTypes.d.ts.map +1 -1
  132. package/lib/simple-tree/schemaTypes.js +10 -0
  133. package/lib/simple-tree/schemaTypes.js.map +1 -1
  134. package/package.json +32 -22
  135. package/src/core/index.ts +1 -0
  136. package/src/core/rebase/index.ts +1 -0
  137. package/src/core/rebase/types.ts +11 -0
  138. package/src/core/schema-stored/schema.ts +1 -0
  139. package/src/feature-libraries/modular-schema/modularChangeFamily.ts +97 -10
  140. package/src/feature-libraries/sequence-field/compose.ts +3 -0
  141. package/src/feature-libraries/sequence-field/utils.ts +2 -4
  142. package/src/index.ts +1 -0
  143. package/src/packageVersion.ts +1 -1
  144. package/src/shared-tree/schematizingTreeView.ts +4 -0
  145. package/src/simple-tree/api/jsonSchema.ts +19 -17
  146. package/src/simple-tree/api/schemaFactory.ts +12 -6
  147. package/src/simple-tree/api/simpleSchema.ts +5 -0
  148. package/src/simple-tree/api/simpleSchemaToJsonSchema.ts +40 -19
  149. package/src/simple-tree/api/tree.ts +5 -0
  150. package/src/simple-tree/api/viewSchemaToSimpleSchema.ts +7 -2
  151. package/src/simple-tree/index.ts +1 -0
  152. package/src/simple-tree/schemaTypes.ts +55 -5
@@ -38,6 +38,7 @@ import {
38
38
  revisionMetadataSourceFromInfo,
39
39
  areEqualChangeAtomIds,
40
40
  type ChangeAtomId,
41
+ areEqualChangeAtomIdOpts,
41
42
  } from "../../core/index.js";
42
43
  import {
43
44
  type IdAllocationState,
@@ -263,8 +264,6 @@ export class ModularChangeFamily
263
264
  const genId: IdAllocator = idAllocatorFromState(idState);
264
265
  const revisionMetadata: RevisionMetadataSource = revisionMetadataSourceFromInfo(revInfos);
265
266
 
266
- const crossFieldTable = newComposeTable(change1, change2);
267
-
268
267
  // We merge nodeChanges, nodeToParent, and nodeAliases from the two changesets.
269
268
  // The merged tables will have correct entries for all nodes which are only referenced in one of the input changesets.
270
269
  // During composeFieldMaps and composeInvalidatedElements we will find all nodes referenced in both input changesets
@@ -284,9 +283,12 @@ export class ModularChangeFamily
284
283
  mergeBTrees(change1.nodeAliases, change2.nodeAliases),
285
284
  );
286
285
 
286
+ const crossFieldTable = newComposeTable(change1, change2, composedNodeToParent);
287
+
287
288
  const composedFields = this.composeFieldMaps(
288
289
  change1.fieldChanges,
289
290
  change2.fieldChanges,
291
+ undefined,
290
292
  genId,
291
293
  crossFieldTable,
292
294
  revisionMetadata,
@@ -321,7 +323,7 @@ export class ModularChangeFamily
321
323
  ): void {
322
324
  const context = crossFieldTable.fieldToContext.get(fieldChange);
323
325
  assert(context !== undefined, 0x8cc /* Should have context for every invalidated field */);
324
- const { change1: fieldChange1, change2: fieldChange2, composedChange } = context;
326
+ const { fieldId, change1: fieldChange1, change2: fieldChange2, composedChange } = context;
325
327
 
326
328
  const rebaser = getChangeHandler(this.fieldKinds, composedChange.fieldKind).rebaser;
327
329
  const composeNodes = (child1: NodeId | undefined, child2: NodeId | undefined): NodeId => {
@@ -342,7 +344,7 @@ export class ModularChangeFamily
342
344
  fieldChange2,
343
345
  composeNodes,
344
346
  genId,
345
- new ComposeManager(crossFieldTable, fieldChange, false),
347
+ new ComposeManager(crossFieldTable, fieldChange, fieldId, false),
346
348
  revisionMetadata,
347
349
  );
348
350
  composedChange.change = brand(amendedChange);
@@ -470,7 +472,14 @@ export class ModularChangeFamily
470
472
  ? [fieldChange, emptyChange]
471
473
  : [emptyChange, fieldChange];
472
474
 
473
- const composedField = this.composeFieldChanges(change1, change2, genId, table, metadata);
475
+ const composedField = this.composeFieldChanges(
476
+ fieldId,
477
+ change1,
478
+ change2,
479
+ genId,
480
+ table,
481
+ metadata,
482
+ );
474
483
 
475
484
  if (fieldId.nodeId === undefined) {
476
485
  composedFields.set(fieldId.field, composedField);
@@ -499,6 +508,7 @@ export class ModularChangeFamily
499
508
  private composeFieldMaps(
500
509
  change1: FieldChangeMap | undefined,
501
510
  change2: FieldChangeMap | undefined,
511
+ parentId: NodeId | undefined,
502
512
  genId: IdAllocator,
503
513
  crossFieldTable: ComposeTable,
504
514
  revisionMetadata: RevisionMetadataSource,
@@ -509,10 +519,12 @@ export class ModularChangeFamily
509
519
  }
510
520
 
511
521
  for (const [field, fieldChange1] of change1) {
522
+ const fieldId: FieldId = { nodeId: parentId, field };
512
523
  const fieldChange2 = change2.get(field);
513
524
  const composedField =
514
525
  fieldChange2 !== undefined
515
526
  ? this.composeFieldChanges(
527
+ fieldId,
516
528
  fieldChange1,
517
529
  fieldChange2,
518
530
  genId,
@@ -545,6 +557,7 @@ export class ModularChangeFamily
545
557
  * Any composed `FieldChange` which is invalidated by new cross-field information will be added to `crossFieldTable.invalidatedFields`.
546
558
  */
547
559
  private composeFieldChanges(
560
+ fieldId: FieldId,
548
561
  change1: FieldChange,
549
562
  change2: FieldChange,
550
563
  idAllocator: IdAllocator,
@@ -558,7 +571,7 @@ export class ModularChangeFamily
558
571
  change2: change2Normalized,
559
572
  } = this.normalizeFieldChanges(change1, change2, idAllocator, revisionMetadata);
560
573
 
561
- const manager = new ComposeManager(crossFieldTable, change1);
574
+ const manager = new ComposeManager(crossFieldTable, change1, fieldId);
562
575
 
563
576
  const composedChange = changeHandler.rebaser.compose(
564
577
  change1Normalized,
@@ -581,6 +594,7 @@ export class ModularChangeFamily
581
594
  };
582
595
 
583
596
  crossFieldTable.fieldToContext.set(change1, {
597
+ fieldId,
584
598
  change1: change1Normalized,
585
599
  change2: change2Normalized,
586
600
  composedChange: composedField,
@@ -605,6 +619,7 @@ export class ModularChangeFamily
605
619
  const nodeChangeset1 = nodeChangeFromId(nodeChanges1, id1);
606
620
  const nodeChangeset2 = nodeChangeFromId(nodeChanges2, id2);
607
621
  const composedNodeChangeset = this.composeNodeChanges(
622
+ id1,
608
623
  nodeChangeset1,
609
624
  nodeChangeset2,
610
625
  idAllocator,
@@ -627,6 +642,7 @@ export class ModularChangeFamily
627
642
  }
628
643
 
629
644
  private composeNodeChanges(
645
+ nodeId: NodeId,
630
646
  change1: NodeChangeset,
631
647
  change2: NodeChangeset,
632
648
  genId: IdAllocator,
@@ -638,6 +654,7 @@ export class ModularChangeFamily
638
654
  const composedFieldChanges = this.composeFieldMaps(
639
655
  change1.fieldChanges,
640
656
  change2.fieldChanges,
657
+ nodeId,
641
658
  genId,
642
659
  crossFieldTable,
643
660
  revisionMetadata,
@@ -887,7 +904,7 @@ export class ModularChangeFamily
887
904
  rebasedNodes,
888
905
  );
889
906
 
890
- return makeModularChangeset(
907
+ const rebased = makeModularChangeset(
891
908
  this.pruneFieldMap(rebasedFields, rebasedNodes),
892
909
  rebasedNodes,
893
910
  crossFieldTable.rebasedNodeToParent,
@@ -900,6 +917,8 @@ export class ModularChangeFamily
900
917
  change.destroys,
901
918
  change.refreshers,
902
919
  );
920
+
921
+ return rebased;
903
922
  }
904
923
 
905
924
  // This performs a first pass on all fields which have both new and base changes.
@@ -1584,6 +1603,57 @@ export class ModularChangeFamily
1584
1603
  const emptyChange = getChangeHandler(this.fieldKinds, fieldKind).createEmpty();
1585
1604
  return { fieldKind, change: brand(emptyChange) };
1586
1605
  }
1606
+
1607
+ public validateChangeset(change: ModularChangeset): void {
1608
+ let numNodes = this.validateFieldChanges(change, change.fieldChanges, undefined);
1609
+
1610
+ for (const [[revision, localId], node] of change.nodeChanges.entries()) {
1611
+ if (node.fieldChanges === undefined) {
1612
+ continue;
1613
+ }
1614
+
1615
+ const nodeId: NodeId = { revision, localId };
1616
+ const numChildren = this.validateFieldChanges(change, node.fieldChanges, nodeId);
1617
+
1618
+ numNodes += numChildren;
1619
+ }
1620
+
1621
+ assert(numNodes === change.nodeChanges.size, "Node table contains unparented nodes");
1622
+ }
1623
+
1624
+ /**
1625
+ * Asserts that each child and cross field key in each field has a correct entry in
1626
+ * `nodeToParent` or `crossFieldKeyTable`.
1627
+ * @returns the number of children found.
1628
+ */
1629
+ private validateFieldChanges(
1630
+ change: ModularChangeset,
1631
+ fieldChanges: FieldChangeMap,
1632
+ nodeParent: NodeId | undefined,
1633
+ ): number {
1634
+ let numChildren = 0;
1635
+ for (const [field, fieldChange] of fieldChanges.entries()) {
1636
+ const fieldId = { nodeId: nodeParent, field };
1637
+ const handler = getChangeHandler(this.fieldKinds, fieldChange.fieldKind);
1638
+ for (const [child, _index] of handler.getNestedChanges(fieldChange.change)) {
1639
+ const parentFieldId = getParentFieldId(change, child);
1640
+ assert(areEqualFieldIds(parentFieldId, fieldId), "Inconsistent node parentage");
1641
+ numChildren += 1;
1642
+ }
1643
+
1644
+ for (const keyRange of handler.getCrossFieldKeys(fieldChange.change)) {
1645
+ const fields = getFieldsForCrossFieldKey(change, keyRange);
1646
+ assert(
1647
+ fields.length === 1 &&
1648
+ fields[0] !== undefined &&
1649
+ areEqualFieldIds(fields[0], fieldId),
1650
+ "Inconsistent cross field keys",
1651
+ );
1652
+ }
1653
+ }
1654
+
1655
+ return numChildren;
1656
+ }
1587
1657
  }
1588
1658
 
1589
1659
  function replaceCrossFieldKeyTableRevisions(
@@ -2059,6 +2129,7 @@ interface RebaseFieldContext {
2059
2129
  function newComposeTable(
2060
2130
  baseChange: ModularChangeset,
2061
2131
  newChange: ModularChangeset,
2132
+ composedNodeToParent: ChangeAtomIdBTree<FieldId>,
2062
2133
  ): ComposeTable {
2063
2134
  return {
2064
2135
  ...newCrossFieldTable<FieldChange>(),
@@ -2068,6 +2139,7 @@ function newComposeTable(
2068
2139
  newFieldToBaseField: new Map(),
2069
2140
  newToBaseNodeId: newTupleBTree(),
2070
2141
  composedNodes: new Set(),
2142
+ composedNodeToParent,
2071
2143
  pendingCompositions: {
2072
2144
  nodeIdsToCompose: [],
2073
2145
  affectedBaseFields: newTupleBTree(),
@@ -2087,6 +2159,7 @@ interface ComposeTable extends CrossFieldTable<FieldChange> {
2087
2159
  readonly newFieldToBaseField: Map<FieldChange, FieldChange>;
2088
2160
  readonly newToBaseNodeId: ChangeAtomIdBTree<NodeId>;
2089
2161
  readonly composedNodes: Set<NodeChangeset>;
2162
+ readonly composedNodeToParent: ChangeAtomIdBTree<FieldId>;
2090
2163
  readonly pendingCompositions: PendingCompositions;
2091
2164
  }
2092
2165
 
@@ -2109,6 +2182,10 @@ interface PendingCompositions {
2109
2182
  }
2110
2183
 
2111
2184
  interface ComposeFieldContext {
2185
+ /**
2186
+ * The field ID for this field in the composed changeset.
2187
+ */
2188
+ fieldId: FieldId;
2112
2189
  change1: FieldChangeset;
2113
2190
  change2: FieldChangeset;
2114
2191
  composedChange: FieldChange;
@@ -2324,7 +2401,12 @@ class RebaseManager extends CrossFieldManagerI<FieldChange> {
2324
2401
 
2325
2402
  // TODO: Deduplicate this with RebaseTable
2326
2403
  class ComposeManager extends CrossFieldManagerI<FieldChange> {
2327
- public constructor(table: ComposeTable, currentField: FieldChange, allowInval = true) {
2404
+ public constructor(
2405
+ table: ComposeTable,
2406
+ currentField: FieldChange,
2407
+ private readonly fieldId: FieldId,
2408
+ allowInval = true,
2409
+ ) {
2328
2410
  super(table, currentField, allowInval);
2329
2411
  }
2330
2412
 
@@ -2377,15 +2459,16 @@ class ComposeManager extends CrossFieldManagerI<FieldChange> {
2377
2459
  }
2378
2460
 
2379
2461
  public override onMoveIn(id: ChangeAtomId): void {
2380
- throw new Error("Method not implemented.");
2462
+ setInChangeAtomIdMap(this.table.composedNodeToParent, id, this.fieldId);
2381
2463
  }
2464
+
2382
2465
  public override moveKey(
2383
2466
  target: CrossFieldTarget,
2384
2467
  revision: RevisionTag | undefined,
2385
2468
  id: ChangesetLocalId,
2386
2469
  count: number,
2387
2470
  ): void {
2388
- throw new Error("Method not implemented.");
2471
+ throw new Error("Moving cross-field keys during compose is currently unsupported");
2389
2472
  }
2390
2473
 
2391
2474
  private get table(): ComposeTable {
@@ -3022,3 +3105,7 @@ function getFromChangeAtomIdMap<T>(
3022
3105
  function setInChangeAtomIdMap<T>(map: ChangeAtomIdBTree<T>, id: ChangeAtomId, value: T): void {
3023
3106
  map.set([id.revision, id.localId], value);
3024
3107
  }
3108
+
3109
+ function areEqualFieldIds(a: FieldId, b: FieldId): boolean {
3110
+ return areEqualChangeAtomIdOpts(a.nodeId, b.nodeId) && a.field === b.field;
3111
+ }
@@ -576,6 +576,9 @@ export class ComposeQueue {
576
576
  private dequeueBase(length: number = Infinity): ComposeMarks {
577
577
  const baseMark = this.baseMarks.dequeueUpTo(length);
578
578
  const movedChanges = getMovedChangesFromMark(this.moveEffects, baseMark);
579
+ if (movedChanges !== undefined) {
580
+ this.moveEffects.onMoveIn(movedChanges);
581
+ }
579
582
 
580
583
  const newMark = createNoopMark(baseMark.count, movedChanges, getOutputCellId(baseMark));
581
584
  return { baseMark, newMark };
@@ -10,6 +10,7 @@ import {
10
10
  type ChangesetLocalId,
11
11
  type RevisionMetadataSource,
12
12
  type RevisionTag,
13
+ areEqualChangeAtomIdOpts,
13
14
  areEqualChangeAtomIds,
14
15
  makeChangeAtomId,
15
16
  } from "../../core/index.js";
@@ -118,10 +119,7 @@ export function isActiveReattach(
118
119
  }
119
120
 
120
121
  export function areEqualCellIds(a: CellId | undefined, b: CellId | undefined): boolean {
121
- if (a === undefined || b === undefined) {
122
- return a === b;
123
- }
124
- return areEqualChangeAtomIds(a, b);
122
+ return areEqualChangeAtomIdOpts(a, b);
125
123
  }
126
124
 
127
125
  export function getInputCellId(mark: Mark): CellId | undefined {
package/src/index.ts CHANGED
@@ -82,6 +82,7 @@ export {
82
82
  type TreeLeafValue,
83
83
  FieldKind,
84
84
  FieldSchema,
85
+ type FieldSchemaMetadata,
85
86
  type ImplicitAllowedTypes,
86
87
  type InsertableTreeFieldFromImplicitField,
87
88
  type InsertableTypedNode,
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/tree";
9
- export const pkgVersion = "2.3.0";
9
+ export const pkgVersion = "2.4.0-294316";
@@ -123,6 +123,10 @@ export class SchematizingSimpleTreeView<in out TRootSchema extends ImplicitField
123
123
  );
124
124
  }
125
125
 
126
+ public get schema(): TRootSchema {
127
+ return this.config.schema;
128
+ }
129
+
126
130
  public initialize(content: InsertableTreeFieldFromImplicitField<TRootSchema>): void {
127
131
  this.ensureUndisposed();
128
132
 
@@ -103,18 +103,10 @@ export interface JsonObjectNodeSchema extends JsonNodeSchemaBase<NodeKind.Object
103
103
  export interface JsonArrayNodeSchema extends JsonNodeSchemaBase<NodeKind.Array, "array"> {
104
104
  /**
105
105
  * The kinds of items allowed under the array.
106
- * @remarks Always represented via references to {@link JsonTreeSchema.$defs}.
107
106
  *
108
107
  * @see {@link https://json-schema.org/draft/2020-12/json-schema-core#name-items}.
109
108
  */
110
- readonly items: {
111
- /**
112
- * The kinds of items allowed under the array.
113
- * @remarks Always represented via references to {@link JsonTreeSchema.$defs}.
114
- * @see {@link https://json-schema.org/draft/2020-12/json-schema-core#name-anyof}.
115
- */
116
- anyOf: JsonSchemaRef[];
117
- };
109
+ readonly items: JsonFieldSchema;
118
110
  }
119
111
 
120
112
  /**
@@ -191,14 +183,24 @@ export type JsonNodeSchema =
191
183
  * @sealed
192
184
  * @alpha
193
185
  */
194
- export interface JsonFieldSchema {
186
+ export type JsonFieldSchema = {
195
187
  /**
196
- * The kinds of items allowed under the field.
197
- * @remarks Always represented via references to {@link JsonTreeSchema.$defs}.
198
- * @see {@link https://json-schema.org/draft/2020-12/json-schema-core#name-anyof}.
188
+ * Description of the field.
189
+ * @remarks Derived from {@link FieldSchemaMetadata.description}.
190
+ * @see {@link https://json-schema.org/draft/2020-12/json-schema-validation#name-title-and-description}
199
191
  */
200
- readonly anyOf: JsonSchemaRef[];
201
- }
192
+ readonly description?: string | undefined;
193
+ } & (
194
+ | {
195
+ /**
196
+ * The kinds of items allowed under the field, for polymorphic types.
197
+ * @remarks Always represented via references to {@link JsonTreeSchema.$defs}.
198
+ * @see {@link https://json-schema.org/draft/2020-12/json-schema-core#name-anyof}.
199
+ */
200
+ readonly anyOf: JsonSchemaRef[];
201
+ }
202
+ | JsonSchemaRef
203
+ );
202
204
 
203
205
  /**
204
206
  * {@link https://json-schema.org/draft/2020-12/json-schema-core | JSON Schema} representation of a tree schema.
@@ -221,10 +223,10 @@ export interface JsonFieldSchema {
221
223
  * @sealed
222
224
  * @alpha
223
225
  */
224
- export interface JsonTreeSchema extends JsonFieldSchema {
226
+ export type JsonTreeSchema = JsonFieldSchema & {
225
227
  /**
226
228
  * The set of definitions reachable from this schema's root.
227
229
  * @see {@link https://json-schema.org/draft/2020-12/json-schema-core#name-schema-re-use-with-defs}
228
230
  */
229
231
  readonly $defs: Record<JsonSchemaId, JsonNodeSchema>;
230
- }
232
+ };
@@ -552,11 +552,14 @@ export class SchemaFactory<
552
552
  *
553
553
  * @param t - The types allowed under the field.
554
554
  * @param props - Optional properties to associate with the field.
555
+ *
556
+ * @typeParam TCustomMetadata - Custom metadata properties to associate with the field.
557
+ * See {@link FieldSchemaMetadata.custom}.
555
558
  */
556
- public optional<const T extends ImplicitAllowedTypes>(
559
+ public optional<const T extends ImplicitAllowedTypes, const TCustomMetadata = unknown>(
557
560
  t: T,
558
- props?: Omit<FieldProps, "defaultProvider">,
559
- ): FieldSchema<FieldKind.Optional, T> {
561
+ props?: Omit<FieldProps<TCustomMetadata>, "defaultProvider">,
562
+ ): FieldSchema<FieldKind.Optional, T, TCustomMetadata> {
560
563
  const defaultOptionalProvider: DefaultProvider = getDefaultProvider(() => {
561
564
  return undefined;
562
565
  });
@@ -575,11 +578,14 @@ export class SchemaFactory<
575
578
  * @remarks
576
579
  * Fields are required by default, but this API can be used to make the required nature explicit in the schema,
577
580
  * and allows associating custom {@link FieldProps | properties} with the field.
581
+ *
582
+ * @typeParam TCustomMetadata - Custom metadata properties to associate with the field.
583
+ * See {@link FieldSchemaMetadata.custom}.
578
584
  */
579
- public required<const T extends ImplicitAllowedTypes>(
585
+ public required<const T extends ImplicitAllowedTypes, const TCustomMetadata = unknown>(
580
586
  t: T,
581
- props?: Omit<FieldProps, "defaultProvider">,
582
- ): FieldSchema<FieldKind.Required, T> {
587
+ props?: Omit<FieldProps<TCustomMetadata>, "defaultProvider">,
588
+ ): FieldSchema<FieldKind.Required, T, TCustomMetadata> {
583
589
  return createFieldSchema(FieldKind.Required, t, props);
584
590
  }
585
591
 
@@ -110,6 +110,11 @@ export interface SimpleFieldSchema {
110
110
  * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}.
111
111
  */
112
112
  readonly allowedTypes: ReadonlySet<string>;
113
+
114
+ /**
115
+ * {@inheritDoc FieldSchemaMetadata.description}
116
+ */
117
+ readonly description?: string | undefined;
113
118
  }
114
119
 
115
120
  /**
@@ -3,10 +3,10 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { unreachableCase } from "@fluidframework/core-utils/internal";
6
+ import { oob, unreachableCase } from "@fluidframework/core-utils/internal";
7
7
  import { UsageError } from "@fluidframework/telemetry-utils/internal";
8
8
  import { ValueSchema } from "../../core/index.js";
9
- import { getOrCreate } from "../../util/index.js";
9
+ import { getOrCreate, type Mutable } from "../../util/index.js";
10
10
  import type {
11
11
  JsonArrayNodeSchema,
12
12
  JsonFieldSchema,
@@ -37,15 +37,20 @@ import { NodeKind } from "../core/index.js";
37
37
  export function toJsonSchema(schema: SimpleTreeSchema): JsonTreeSchema {
38
38
  const definitions = convertDefinitions(schema.definitions);
39
39
 
40
- const anyOf: JsonSchemaRef[] = [];
40
+ const allowedTypes: JsonSchemaRef[] = [];
41
41
  for (const allowedType of schema.allowedTypes) {
42
- anyOf.push(createSchemaRef(allowedType));
42
+ allowedTypes.push(createSchemaRef(allowedType));
43
43
  }
44
44
 
45
- return {
46
- $defs: definitions,
47
- anyOf,
48
- };
45
+ return allowedTypes.length === 1
46
+ ? {
47
+ ...(allowedTypes[0] ?? oob()),
48
+ $defs: definitions,
49
+ }
50
+ : {
51
+ $defs: definitions,
52
+ anyOf: allowedTypes,
53
+ };
49
54
  }
50
55
 
51
56
  function convertDefinitions(
@@ -90,12 +95,14 @@ function convertArrayNodeSchema(schema: SimpleArrayNodeSchema): JsonArrayNodeSch
90
95
  schema.allowedTypes.forEach((type) => {
91
96
  allowedTypes.push(createSchemaRef(type));
92
97
  });
98
+
99
+ const items: JsonFieldSchema =
100
+ allowedTypes.length === 1 ? allowedTypes[0] ?? oob() : { anyOf: allowedTypes };
101
+
93
102
  return {
94
103
  type: "array",
95
104
  _treeNodeSchemaKind: NodeKind.Array,
96
- items: {
97
- anyOf: allowedTypes,
98
- },
105
+ items,
99
106
  };
100
107
  }
101
108
 
@@ -130,14 +137,25 @@ function convertObjectNodeSchema(schema: SimpleObjectNodeSchema): JsonObjectNode
130
137
  const properties: Record<string, JsonFieldSchema> = {};
131
138
  const required: string[] = [];
132
139
  for (const [key, value] of Object.entries(schema.fields)) {
133
- const anyOf: JsonSchemaRef[] = [];
140
+ const allowedTypes: JsonSchemaRef[] = [];
134
141
  for (const allowedType of value.allowedTypes) {
135
- anyOf.push(createSchemaRef(allowedType));
142
+ allowedTypes.push(createSchemaRef(allowedType));
143
+ }
144
+
145
+ const output: Mutable<JsonFieldSchema> =
146
+ allowedTypes.length === 1
147
+ ? allowedTypes[0] ?? oob()
148
+ : {
149
+ anyOf: allowedTypes,
150
+ };
151
+
152
+ // Don't include "description" property at all if it's not present in the input.
153
+ if (value.description !== undefined) {
154
+ output.description = value.description;
136
155
  }
137
156
 
138
- properties[key] = {
139
- anyOf,
140
- };
157
+ properties[key] = output;
158
+
141
159
  if (value.kind === FieldKind.Required) {
142
160
  required.push(key);
143
161
  }
@@ -160,9 +178,12 @@ function convertMapNodeSchema(schema: SimpleMapNodeSchema): JsonMapNodeSchema {
160
178
  type: "object",
161
179
  _treeNodeSchemaKind: NodeKind.Map,
162
180
  patternProperties: {
163
- "^.*$": {
164
- anyOf: allowedTypes,
165
- },
181
+ "^.*$":
182
+ allowedTypes.length === 1
183
+ ? allowedTypes[0] ?? oob()
184
+ : {
185
+ anyOf: allowedTypes,
186
+ },
166
187
  },
167
188
  };
168
189
  }
@@ -411,6 +411,11 @@ export interface TreeView<TSchema extends ImplicitFieldSchema> extends IDisposab
411
411
  * Events for the tree.
412
412
  */
413
413
  readonly events: Listenable<TreeViewEvents>;
414
+
415
+ /**
416
+ * The view schema used by this TreeView.
417
+ */
418
+ readonly schema: TSchema;
414
419
  }
415
420
 
416
421
  /**
@@ -19,7 +19,7 @@ import type {
19
19
  SimpleTreeSchema,
20
20
  } from "./simpleSchema.js";
21
21
  import type { ValueSchema } from "../../core/index.js";
22
- import { getOrCreate } from "../../util/index.js";
22
+ import { getOrCreate, type Mutable } from "../../util/index.js";
23
23
  import { isObjectNodeSchema, type ObjectNodeSchema } from "../objectNodeTypes.js";
24
24
  import { NodeKind, type TreeNodeSchema } from "../core/index.js";
25
25
 
@@ -126,11 +126,16 @@ function fieldSchemaToSimpleSchema(schema: FieldSchema): SimpleFieldSchema {
126
126
  }
127
127
 
128
128
  const allowedTypes = allowedTypesFromFieldSchema(schema);
129
- const result = {
129
+ const result: Mutable<SimpleFieldSchema> = {
130
130
  kind: schema.kind,
131
131
  allowedTypes,
132
132
  };
133
133
 
134
+ // Don't include "description" property at all if it's not present.
135
+ if (schema.metadata?.description !== undefined) {
136
+ result.description = schema.metadata.description;
137
+ }
138
+
134
139
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
135
140
  (schema as any)[simpleFieldSchemaCacheSymbol] = result;
136
141
 
@@ -84,6 +84,7 @@ export {
84
84
  type FieldProps,
85
85
  normalizeFieldSchema,
86
86
  type ApplyKind,
87
+ type FieldSchemaMetadata,
87
88
  } from "./schemaTypes.js";
88
89
  export { getOrCreateInnerNode } from "./proxyBinding.js";
89
90
  export { toFlexSchema } from "./toFlexSchema.js";