@fluidframework/tree 2.10.0-305357 → 2.10.0-306579

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 (234) hide show
  1. package/.eslintrc.cjs +56 -25
  2. package/api-report/tree.alpha.api.md +3 -2
  3. package/api-report/tree.beta.api.md +3 -2
  4. package/api-report/tree.legacy.alpha.api.md +3 -2
  5. package/api-report/tree.legacy.public.api.md +3 -2
  6. package/api-report/tree.public.api.md +3 -2
  7. package/dist/core/forest/forest.d.ts +5 -1
  8. package/dist/core/forest/forest.d.ts.map +1 -1
  9. package/dist/core/forest/forest.js.map +1 -1
  10. package/dist/core/index.d.ts +1 -1
  11. package/dist/core/index.d.ts.map +1 -1
  12. package/dist/core/index.js.map +1 -1
  13. package/dist/core/schema-stored/storedSchemaRepository.d.ts +7 -3
  14. package/dist/core/schema-stored/storedSchemaRepository.d.ts.map +1 -1
  15. package/dist/core/schema-stored/storedSchemaRepository.js +4 -6
  16. package/dist/core/schema-stored/storedSchemaRepository.js.map +1 -1
  17. package/dist/core/tree/anchorSet.d.ts +8 -5
  18. package/dist/core/tree/anchorSet.d.ts.map +1 -1
  19. package/dist/core/tree/anchorSet.js +12 -11
  20. package/dist/core/tree/anchorSet.js.map +1 -1
  21. package/dist/events/emitter.d.ts +21 -9
  22. package/dist/events/emitter.d.ts.map +1 -1
  23. package/dist/events/emitter.js +36 -21
  24. package/dist/events/emitter.js.map +1 -1
  25. package/dist/events/listeners.d.ts +16 -5
  26. package/dist/events/listeners.d.ts.map +1 -1
  27. package/dist/events/listeners.js.map +1 -1
  28. package/dist/feature-libraries/chunked-forest/chunkTree.js +1 -1
  29. package/dist/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
  30. package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts +3 -2
  31. package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  32. package/dist/feature-libraries/chunked-forest/chunkedForest.js +14 -9
  33. package/dist/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  34. package/dist/feature-libraries/flex-tree/context.d.ts +3 -2
  35. package/dist/feature-libraries/flex-tree/context.d.ts.map +1 -1
  36. package/dist/feature-libraries/flex-tree/context.js +3 -3
  37. package/dist/feature-libraries/flex-tree/context.js.map +1 -1
  38. package/dist/feature-libraries/flex-tree/lazyField.js +1 -1
  39. package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
  40. package/dist/feature-libraries/flex-tree/lazyNode.js +1 -1
  41. package/dist/feature-libraries/flex-tree/lazyNode.js.map +1 -1
  42. package/dist/feature-libraries/flex-tree/utilities.js +1 -1
  43. package/dist/feature-libraries/flex-tree/utilities.js.map +1 -1
  44. package/dist/feature-libraries/modular-schema/comparison.d.ts.map +1 -1
  45. package/dist/feature-libraries/modular-schema/comparison.js +3 -0
  46. package/dist/feature-libraries/modular-schema/comparison.js.map +1 -1
  47. package/dist/feature-libraries/modular-schema/discrepancies.d.ts +2 -2
  48. package/dist/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -1
  49. package/dist/feature-libraries/modular-schema/discrepancies.js +90 -44
  50. package/dist/feature-libraries/modular-schema/discrepancies.js.map +1 -1
  51. package/dist/feature-libraries/modular-schema/genericFieldKind.js +2 -2
  52. package/dist/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  53. package/dist/feature-libraries/modular-schema/modularChangeFamily.js +1 -1
  54. package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  55. package/dist/feature-libraries/object-forest/objectForest.d.ts +2 -2
  56. package/dist/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
  57. package/dist/feature-libraries/object-forest/objectForest.js +6 -8
  58. package/dist/feature-libraries/object-forest/objectForest.js.map +1 -1
  59. package/dist/feature-libraries/schema-index/schemaSummarizer.js +1 -1
  60. package/dist/feature-libraries/schema-index/schemaSummarizer.js.map +1 -1
  61. package/dist/feature-libraries/sequence-field/compose.js +2 -2
  62. package/dist/feature-libraries/sequence-field/compose.js.map +1 -1
  63. package/dist/feature-libraries/sequence-field/markListFactory.js +1 -1
  64. package/dist/feature-libraries/sequence-field/markListFactory.js.map +1 -1
  65. package/dist/packageVersion.d.ts +1 -1
  66. package/dist/packageVersion.js +1 -1
  67. package/dist/packageVersion.js.map +1 -1
  68. package/dist/shared-tree/schematizingTreeView.js +2 -2
  69. package/dist/shared-tree/schematizingTreeView.js.map +1 -1
  70. package/dist/shared-tree/treeApi.js +2 -2
  71. package/dist/shared-tree/treeApi.js.map +1 -1
  72. package/dist/shared-tree/treeCheckout.js +7 -7
  73. package/dist/shared-tree/treeCheckout.js.map +1 -1
  74. package/dist/shared-tree-core/branch.d.ts +7 -7
  75. package/dist/shared-tree-core/branch.d.ts.map +1 -1
  76. package/dist/shared-tree-core/branch.js +35 -25
  77. package/dist/shared-tree-core/branch.js.map +1 -1
  78. package/dist/shared-tree-core/editManager.js +4 -4
  79. package/dist/shared-tree-core/editManager.js.map +1 -1
  80. package/dist/shared-tree-core/sharedTreeCore.js +5 -5
  81. package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
  82. package/dist/simple-tree/api/schemaFactory.d.ts +66 -10
  83. package/dist/simple-tree/api/schemaFactory.d.ts.map +1 -1
  84. package/dist/simple-tree/api/schemaFactory.js +34 -9
  85. package/dist/simple-tree/api/schemaFactory.js.map +1 -1
  86. package/dist/simple-tree/api/treeNodeApi.js +4 -4
  87. package/dist/simple-tree/api/treeNodeApi.js.map +1 -1
  88. package/dist/simple-tree/arrayNode.js +1 -1
  89. package/dist/simple-tree/arrayNode.js.map +1 -1
  90. package/dist/simple-tree/core/treeNodeKernel.d.ts +3 -3
  91. package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  92. package/dist/simple-tree/core/treeNodeKernel.js +7 -8
  93. package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
  94. package/dist/simple-tree/proxies.js +1 -1
  95. package/dist/simple-tree/proxies.js.map +1 -1
  96. package/dist/simple-tree/schemaTypes.d.ts +26 -1
  97. package/dist/simple-tree/schemaTypes.d.ts.map +1 -1
  98. package/dist/simple-tree/schemaTypes.js.map +1 -1
  99. package/dist/simple-tree/treeNodeValid.js +2 -2
  100. package/dist/simple-tree/treeNodeValid.js.map +1 -1
  101. package/dist/util/nestedMap.d.ts.map +1 -1
  102. package/dist/util/nestedMap.js.map +1 -1
  103. package/lib/core/forest/forest.d.ts +5 -1
  104. package/lib/core/forest/forest.d.ts.map +1 -1
  105. package/lib/core/forest/forest.js.map +1 -1
  106. package/lib/core/index.d.ts +1 -1
  107. package/lib/core/index.d.ts.map +1 -1
  108. package/lib/core/index.js.map +1 -1
  109. package/lib/core/schema-stored/storedSchemaRepository.d.ts +7 -3
  110. package/lib/core/schema-stored/storedSchemaRepository.d.ts.map +1 -1
  111. package/lib/core/schema-stored/storedSchemaRepository.js +4 -6
  112. package/lib/core/schema-stored/storedSchemaRepository.js.map +1 -1
  113. package/lib/core/tree/anchorSet.d.ts +8 -5
  114. package/lib/core/tree/anchorSet.d.ts.map +1 -1
  115. package/lib/core/tree/anchorSet.js +12 -11
  116. package/lib/core/tree/anchorSet.js.map +1 -1
  117. package/lib/events/emitter.d.ts +21 -9
  118. package/lib/events/emitter.d.ts.map +1 -1
  119. package/lib/events/emitter.js +37 -22
  120. package/lib/events/emitter.js.map +1 -1
  121. package/lib/events/listeners.d.ts +16 -5
  122. package/lib/events/listeners.d.ts.map +1 -1
  123. package/lib/events/listeners.js.map +1 -1
  124. package/lib/feature-libraries/chunked-forest/chunkTree.js +1 -1
  125. package/lib/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
  126. package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts +3 -2
  127. package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  128. package/lib/feature-libraries/chunked-forest/chunkedForest.js +14 -9
  129. package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  130. package/lib/feature-libraries/flex-tree/context.d.ts +3 -2
  131. package/lib/feature-libraries/flex-tree/context.d.ts.map +1 -1
  132. package/lib/feature-libraries/flex-tree/context.js +3 -3
  133. package/lib/feature-libraries/flex-tree/context.js.map +1 -1
  134. package/lib/feature-libraries/flex-tree/lazyField.js +1 -1
  135. package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
  136. package/lib/feature-libraries/flex-tree/lazyNode.js +1 -1
  137. package/lib/feature-libraries/flex-tree/lazyNode.js.map +1 -1
  138. package/lib/feature-libraries/flex-tree/utilities.js +1 -1
  139. package/lib/feature-libraries/flex-tree/utilities.js.map +1 -1
  140. package/lib/feature-libraries/modular-schema/comparison.d.ts.map +1 -1
  141. package/lib/feature-libraries/modular-schema/comparison.js +3 -0
  142. package/lib/feature-libraries/modular-schema/comparison.js.map +1 -1
  143. package/lib/feature-libraries/modular-schema/discrepancies.d.ts +2 -2
  144. package/lib/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -1
  145. package/lib/feature-libraries/modular-schema/discrepancies.js +91 -45
  146. package/lib/feature-libraries/modular-schema/discrepancies.js.map +1 -1
  147. package/lib/feature-libraries/modular-schema/genericFieldKind.js +2 -2
  148. package/lib/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  149. package/lib/feature-libraries/modular-schema/modularChangeFamily.js +1 -1
  150. package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  151. package/lib/feature-libraries/object-forest/objectForest.d.ts +2 -2
  152. package/lib/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
  153. package/lib/feature-libraries/object-forest/objectForest.js +6 -8
  154. package/lib/feature-libraries/object-forest/objectForest.js.map +1 -1
  155. package/lib/feature-libraries/schema-index/schemaSummarizer.js +1 -1
  156. package/lib/feature-libraries/schema-index/schemaSummarizer.js.map +1 -1
  157. package/lib/feature-libraries/sequence-field/compose.js +2 -2
  158. package/lib/feature-libraries/sequence-field/compose.js.map +1 -1
  159. package/lib/feature-libraries/sequence-field/markListFactory.js +1 -1
  160. package/lib/feature-libraries/sequence-field/markListFactory.js.map +1 -1
  161. package/lib/packageVersion.d.ts +1 -1
  162. package/lib/packageVersion.js +1 -1
  163. package/lib/packageVersion.js.map +1 -1
  164. package/lib/shared-tree/schematizingTreeView.js +2 -2
  165. package/lib/shared-tree/schematizingTreeView.js.map +1 -1
  166. package/lib/shared-tree/treeApi.js +2 -2
  167. package/lib/shared-tree/treeApi.js.map +1 -1
  168. package/lib/shared-tree/treeCheckout.js +7 -7
  169. package/lib/shared-tree/treeCheckout.js.map +1 -1
  170. package/lib/shared-tree-core/branch.d.ts +7 -7
  171. package/lib/shared-tree-core/branch.d.ts.map +1 -1
  172. package/lib/shared-tree-core/branch.js +36 -26
  173. package/lib/shared-tree-core/branch.js.map +1 -1
  174. package/lib/shared-tree-core/editManager.js +4 -4
  175. package/lib/shared-tree-core/editManager.js.map +1 -1
  176. package/lib/shared-tree-core/sharedTreeCore.js +5 -5
  177. package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
  178. package/lib/simple-tree/api/schemaFactory.d.ts +66 -10
  179. package/lib/simple-tree/api/schemaFactory.d.ts.map +1 -1
  180. package/lib/simple-tree/api/schemaFactory.js +34 -9
  181. package/lib/simple-tree/api/schemaFactory.js.map +1 -1
  182. package/lib/simple-tree/api/treeNodeApi.js +4 -4
  183. package/lib/simple-tree/api/treeNodeApi.js.map +1 -1
  184. package/lib/simple-tree/arrayNode.js +1 -1
  185. package/lib/simple-tree/arrayNode.js.map +1 -1
  186. package/lib/simple-tree/core/treeNodeKernel.d.ts +3 -3
  187. package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  188. package/lib/simple-tree/core/treeNodeKernel.js +7 -8
  189. package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
  190. package/lib/simple-tree/proxies.js +1 -1
  191. package/lib/simple-tree/proxies.js.map +1 -1
  192. package/lib/simple-tree/schemaTypes.d.ts +26 -1
  193. package/lib/simple-tree/schemaTypes.d.ts.map +1 -1
  194. package/lib/simple-tree/schemaTypes.js.map +1 -1
  195. package/lib/simple-tree/treeNodeValid.js +2 -2
  196. package/lib/simple-tree/treeNodeValid.js.map +1 -1
  197. package/lib/util/nestedMap.d.ts.map +1 -1
  198. package/lib/util/nestedMap.js.map +1 -1
  199. package/package.json +20 -20
  200. package/src/core/forest/forest.ts +6 -1
  201. package/src/core/index.ts +1 -1
  202. package/src/core/schema-stored/storedSchemaRepository.ts +10 -13
  203. package/src/core/tree/anchorSet.ts +13 -20
  204. package/src/events/emitter.ts +45 -24
  205. package/src/events/listeners.ts +17 -5
  206. package/src/feature-libraries/chunked-forest/chunkTree.ts +1 -1
  207. package/src/feature-libraries/chunked-forest/chunkedForest.ts +8 -14
  208. package/src/feature-libraries/flex-tree/context.ts +5 -7
  209. package/src/feature-libraries/flex-tree/lazyField.ts +1 -1
  210. package/src/feature-libraries/flex-tree/lazyNode.ts +1 -1
  211. package/src/feature-libraries/flex-tree/utilities.ts +1 -1
  212. package/src/feature-libraries/modular-schema/comparison.ts +4 -0
  213. package/src/feature-libraries/modular-schema/discrepancies.ts +116 -50
  214. package/src/feature-libraries/modular-schema/genericFieldKind.ts +2 -2
  215. package/src/feature-libraries/modular-schema/modularChangeFamily.ts +1 -1
  216. package/src/feature-libraries/object-forest/objectForest.ts +5 -11
  217. package/src/feature-libraries/schema-index/schemaSummarizer.ts +1 -1
  218. package/src/feature-libraries/sequence-field/compose.ts +2 -2
  219. package/src/feature-libraries/sequence-field/markListFactory.ts +1 -1
  220. package/src/packageVersion.ts +1 -1
  221. package/src/shared-tree/schematizingTreeView.ts +2 -2
  222. package/src/shared-tree/treeApi.ts +2 -2
  223. package/src/shared-tree/treeCheckout.ts +7 -7
  224. package/src/shared-tree-core/branch.ts +30 -30
  225. package/src/shared-tree-core/editManager.ts +4 -4
  226. package/src/shared-tree-core/sharedTreeCore.ts +5 -5
  227. package/src/simple-tree/api/schemaFactory.ts +37 -11
  228. package/src/simple-tree/api/treeNodeApi.ts +4 -4
  229. package/src/simple-tree/arrayNode.ts +1 -1
  230. package/src/simple-tree/core/treeNodeKernel.ts +8 -10
  231. package/src/simple-tree/proxies.ts +1 -1
  232. package/src/simple-tree/schemaTypes.ts +26 -1
  233. package/src/simple-tree/treeNodeValid.ts +2 -2
  234. package/src/util/nestedMap.ts +1 -0
@@ -11,12 +11,14 @@ import {
11
11
  LeafNodeStoredSchema,
12
12
  MapNodeStoredSchema,
13
13
  ObjectNodeStoredSchema,
14
+ storedEmptyFieldSchema,
14
15
  type TreeFieldStoredSchema,
15
16
  type TreeNodeSchemaIdentifier,
16
17
  type TreeStoredSchema,
17
18
  type TreeTypeSet,
18
19
  type ValueSchema,
19
20
  } from "../../core/index.js";
21
+ import { brand } from "../../util/index.js";
20
22
 
21
23
  // TODO:
22
24
  // The comparisons in this file seem redundant with those in comparison.ts.
@@ -79,8 +81,8 @@ export interface AllowedTypeIncompatibility {
79
81
  export interface FieldKindIncompatibility {
80
82
  identifier: string | undefined; // undefined indicates root field schema
81
83
  mismatch: "fieldKind";
82
- view: FieldKindIdentifier | undefined;
83
- stored: FieldKindIdentifier | undefined;
84
+ view: FieldKindIdentifier;
85
+ stored: FieldKindIdentifier;
84
86
  }
85
87
 
86
88
  export interface ValueSchemaIncompatibility {
@@ -348,12 +350,15 @@ function trackObjectNodeDiscrepancies(
348
350
 
349
351
  for (const [fieldKey, fieldStoredSchema] of view.objectNodeFields) {
350
352
  viewFieldKeys.add(fieldKey);
351
- if (!stored.objectNodeFields.has(fieldKey)) {
353
+ if (
354
+ !stored.objectNodeFields.has(fieldKey) &&
355
+ fieldStoredSchema.kind !== storedEmptyFieldSchema.kind
356
+ ) {
352
357
  differences.push({
353
358
  identifier: fieldKey,
354
359
  mismatch: "fieldKind",
355
360
  view: fieldStoredSchema.kind,
356
- stored: undefined,
361
+ stored: storedEmptyFieldSchema.kind,
357
362
  } satisfies FieldKindIncompatibility);
358
363
  } else {
359
364
  differences.push(
@@ -370,12 +375,15 @@ function trackObjectNodeDiscrepancies(
370
375
  if (viewFieldKeys.has(fieldKey)) {
371
376
  continue;
372
377
  }
373
- differences.push({
374
- identifier: fieldKey,
375
- mismatch: "fieldKind",
376
- view: undefined,
377
- stored: fieldStoredSchema.kind,
378
- } satisfies FieldKindIncompatibility);
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);
386
+ }
379
387
  }
380
388
 
381
389
  return differences;
@@ -404,7 +412,11 @@ export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema)
404
412
  for (const incompatibility of incompatibilities) {
405
413
  switch (incompatibility.mismatch) {
406
414
  case "nodeKind": {
407
- return false;
415
+ if (incompatibility.stored !== undefined) {
416
+ // It's fine for the view schema to know of a node type that the stored schema doesn't know about.
417
+ return false;
418
+ }
419
+ break;
408
420
  }
409
421
  case "valueSchema":
410
422
  case "allowedTypes":
@@ -439,17 +451,7 @@ function validateFieldIncompatibility(incompatibility: FieldIncompatibility): bo
439
451
  return incompatibility.stored.length === 0;
440
452
  }
441
453
  case "fieldKind": {
442
- if (incompatibility.stored === undefined) {
443
- // Add an optional field
444
- if (incompatibility.view === "Optional") {
445
- return true;
446
- }
447
- } else {
448
- // Relax the field to make it more general
449
- return compareFieldKind(incompatibility.stored, incompatibility.view);
450
- }
451
-
452
- break;
454
+ return posetLte(incompatibility.stored, incompatibility.view, fieldRealizer);
453
455
  }
454
456
  case "valueSchema": {
455
457
  return false;
@@ -460,41 +462,105 @@ function validateFieldIncompatibility(incompatibility: FieldIncompatibility): bo
460
462
  }
461
463
 
462
464
  /**
463
- * A mapping that defines the order of field kinds for comparison purposes.
464
- * The numeric values indicate the hierarchy or "strength" of each field kind, where lower numbers are more restrictive.
465
- * This is used to determine if one field kind can be considered a superset of another.
465
+ * A linear extension of a partially-ordered set of `T`s. See:
466
+ * https://en.wikipedia.org/wiki/Linear_extension
466
467
  *
467
- * - "Forbidden": The most restrictive, represented by 1. Indicates a forbidden field.
468
- * - "Value": Represented by 2. Indicates a required field with a specific value.
469
- * - "Optional": Represented by 3. Indicates an optional field.
470
- *
471
- * Note:
472
- * - "Sequence": (Currently commented out) was intended to represent a sequence field kind with a value of 4.
473
- * Relaxing non-sequence fields to sequences is not currently supported but may be considered in the future.
474
- *
475
- * TODO: We may need more coverage in realm to prove the correctness of the Forbidden -\> Value transaction
468
+ * The linear extension is represented as a lookup from each poset element to its index in the linear extension.
469
+ */
470
+ type LinearExtension<T> = Map<T, number>;
471
+
472
+ /**
473
+ * A realizer for a partially-ordered set. See:
474
+ * https://en.wikipedia.org/wiki/Order_dimension
475
+ */
476
+ type Realizer<T> = LinearExtension<T>[];
477
+
478
+ /**
479
+ * @privateRemarks
480
+ * TODO: Knowledge of specific field kinds is not appropriate for modular schema.
481
+ * This bit of field comparison should be dependency injected by default-schema if this comparison logic remains in modular-schema
482
+ * (this is analogous to what is done in comparison.ts).
476
483
  */
477
- const fieldKindOrder: { [key: string]: number } = {
478
- "Forbidden": 1,
479
- "Value": 2,
480
- "Optional": 3,
481
- // "Sequence": 4, // Relaxing non-sequence fields to sequences is not currently supported, though we could consider doing so in the future.
484
+ const FieldKindIdentifiers = {
485
+ forbidden: brand<FieldKindIdentifier>("Forbidden"),
486
+ required: brand<FieldKindIdentifier>("Value"),
487
+ identifier: brand<FieldKindIdentifier>("Identifier"),
488
+ optional: brand<FieldKindIdentifier>("Optional"),
489
+ sequence: brand<FieldKindIdentifier>("Sequence"),
482
490
  };
483
491
 
484
- function compareFieldKind(
485
- aKind: FieldKindIdentifier | undefined,
486
- bKind: FieldKindIdentifier | undefined,
487
- ): boolean {
488
- if (aKind === undefined || bKind === undefined) {
489
- return false;
492
+ /**
493
+ * A realizer for the partial order of field kind relaxability.
494
+ *
495
+ * It seems extremely likely that this partial order will remain dimension 2 over time (i.e. the set of allowed relaxations can be visualized
496
+ * with a [dominance drawing](https://en.wikipedia.org/wiki/Dominance_drawing)), so this strategy allows efficient comarison between field kinds
497
+ * without excessive casework.
498
+ *
499
+ * Hasse diagram for the partial order is shown below (lower fields can be relaxed to higher fields):
500
+ * ```
501
+ * sequence
502
+ * |
503
+ * optional
504
+ * | \
505
+ * required forbidden
506
+ * |
507
+ * identifier
508
+ * ```
509
+ */
510
+ const fieldRealizer: Realizer<FieldKindIdentifier> = [
511
+ [
512
+ FieldKindIdentifiers.forbidden,
513
+ FieldKindIdentifiers.identifier,
514
+ FieldKindIdentifiers.required,
515
+ FieldKindIdentifiers.optional,
516
+ FieldKindIdentifiers.sequence,
517
+ ],
518
+ [
519
+ FieldKindIdentifiers.identifier,
520
+ FieldKindIdentifiers.required,
521
+ FieldKindIdentifiers.forbidden,
522
+ FieldKindIdentifiers.optional,
523
+ FieldKindIdentifiers.sequence,
524
+ ],
525
+ ].map((extension) => new Map(extension.map((identifier, index) => [identifier, index])));
526
+
527
+ const PosetComparisonResult = {
528
+ Less: "<",
529
+ Greater: ">",
530
+ Equal: "=",
531
+ Incomparable: "||",
532
+ } as const;
533
+ type PosetComparisonResult =
534
+ (typeof PosetComparisonResult)[keyof typeof PosetComparisonResult];
535
+
536
+ function comparePosetElements<T>(a: T, b: T, realizer: Realizer<T>): PosetComparisonResult {
537
+ let hasLessThanResult = false;
538
+ let hasGreaterThanResult = false;
539
+ for (const extension of realizer) {
540
+ const aIndex = extension.get(a);
541
+ const bIndex = extension.get(b);
542
+ assert(aIndex !== undefined && bIndex !== undefined, "Invalid realizer");
543
+ if (aIndex < bIndex) {
544
+ hasLessThanResult = true;
545
+ } else if (aIndex > bIndex) {
546
+ hasGreaterThanResult = true;
547
+ }
490
548
  }
491
549
 
492
- if (!(aKind in fieldKindOrder) || !(bKind in fieldKindOrder)) {
493
- return false;
494
- }
550
+ return hasLessThanResult
551
+ ? hasGreaterThanResult
552
+ ? PosetComparisonResult.Incomparable
553
+ : PosetComparisonResult.Less
554
+ : hasGreaterThanResult
555
+ ? PosetComparisonResult.Greater
556
+ : PosetComparisonResult.Equal;
557
+ }
495
558
 
496
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
497
- return fieldKindOrder[aKind]! <= fieldKindOrder[bKind]!;
559
+ function posetLte<T>(a: T, b: T, realizer: Realizer<T>): boolean {
560
+ const comparison = comparePosetElements(a, b, realizer);
561
+ return (
562
+ comparison === PosetComparisonResult.Less || comparison === PosetComparisonResult.Equal
563
+ );
498
564
  }
499
565
 
500
566
  function throwUnsupportedNodeType(type: string): never {
@@ -103,8 +103,8 @@ function rebaseGenericChange(
103
103
  break;
104
104
  }
105
105
 
106
- const newIndex = newEntry?.[0] ?? Infinity;
107
- const baseIndex = baseEntry?.[0] ?? Infinity;
106
+ const newIndex = newEntry?.[0] ?? Number.POSITIVE_INFINITY;
107
+ const baseIndex = baseEntry?.[0] ?? Number.POSITIVE_INFINITY;
108
108
  let newNodeChange: NodeId | undefined;
109
109
  let baseNodeChange: NodeId | undefined;
110
110
  let index: number;
@@ -3016,7 +3016,7 @@ function getFirstIntersectingCrossFieldEntry(
3016
3016
  table: CrossFieldKeyTable,
3017
3017
  [target, revision, id, count]: CrossFieldKeyRange,
3018
3018
  ): [CrossFieldKeyRange, FieldId] | undefined {
3019
- const entry = table.nextLowerPair([target, revision, id, Infinity]);
3019
+ const entry = table.nextLowerPair([target, revision, id, Number.POSITIVE_INFINITY]);
3020
3020
  if (entry === undefined) {
3021
3021
  return undefined;
3022
3022
  }
@@ -33,7 +33,7 @@ import {
33
33
  aboveRootPlaceholder,
34
34
  deepCopyMapTree,
35
35
  } from "../../core/index.js";
36
- import { createEmitter } from "../../events/index.js";
36
+ import { createEmitter, type Listenable } from "../../events/index.js";
37
37
  import {
38
38
  assertNonNegativeSafeInteger,
39
39
  assertValidIndex,
@@ -73,7 +73,8 @@ export class ObjectForest implements IEditableForest {
73
73
  // All cursors that are in the "Current" state. Must be empty when editing.
74
74
  public readonly currentCursors: Set<Cursor> = new Set();
75
75
 
76
- private readonly events = createEmitter<ForestEvents>();
76
+ readonly #events = createEmitter<ForestEvents>();
77
+ public readonly events: Listenable<ForestEvents> = this.#events;
77
78
 
78
79
  readonly #roots: MutableMapTree;
79
80
  public get roots(): MapTree {
@@ -98,13 +99,6 @@ export class ObjectForest implements IEditableForest {
98
99
  return this.roots.fields.size === 0;
99
100
  }
100
101
 
101
- public on<K extends keyof ForestEvents>(
102
- eventName: K,
103
- listener: ForestEvents[K],
104
- ): () => void {
105
- return this.events.on(eventName, listener);
106
- }
107
-
108
102
  public clone(_: TreeStoredSchemaSubscription, anchors: AnchorSet): ObjectForest {
109
103
  return new ObjectForest(anchors, this.additionalAsserts, this.roots);
110
104
  }
@@ -133,7 +127,7 @@ export class ObjectForest implements IEditableForest {
133
127
  * This is required for each change since there may be app facing change event handlers which create cursors.
134
128
  */
135
129
  const preEdit = (): void => {
136
- this.events.emit("beforeChange");
130
+ this.#events.emit("beforeChange");
137
131
  assert(
138
132
  this.currentCursors.has(cursor),
139
133
  0x995 /* missing visitor cursor while editing */,
@@ -168,7 +162,7 @@ export class ObjectForest implements IEditableForest {
168
162
  public create(content: ProtoNodes, destination: FieldKey): void {
169
163
  preEdit();
170
164
  this.forest.add(content, destination);
171
- this.forest.events.emit("afterRootFieldCreated", destination);
165
+ this.forest.#events.emit("afterRootFieldCreated", destination);
172
166
  }
173
167
  public attach(source: FieldKey, count: number, destination: PlaceIndex): void {
174
168
  preEdit();
@@ -52,7 +52,7 @@ export class SchemaSummarizer implements Summarizable {
52
52
  collabWindow: CollabWindow,
53
53
  ) {
54
54
  this.codec = makeSchemaCodec(options);
55
- this.schema.on("afterSchemaChange", () => {
55
+ this.schema.events.on("afterSchemaChange", () => {
56
56
  // Invalidate the cache, as we need to regenerate the blob if the schema changes
57
57
  // We are assuming that schema changes from remote ops are valid, as we are in a summarization context.
58
58
  this.schemaIndexLastChangedSeq = collabWindow.getCurrentSeq();
@@ -582,7 +582,7 @@ export class ComposeQueue {
582
582
  }
583
583
  }
584
584
 
585
- private dequeueBase(length: number = Infinity): ComposeMarks {
585
+ private dequeueBase(length: number = Number.POSITIVE_INFINITY): ComposeMarks {
586
586
  const baseMark = this.baseMarks.dequeueUpTo(length);
587
587
  const movedChanges = getMovedChangesFromMark(this.moveEffects, baseMark);
588
588
  if (movedChanges !== undefined) {
@@ -593,7 +593,7 @@ export class ComposeQueue {
593
593
  return { baseMark, newMark };
594
594
  }
595
595
 
596
- private dequeueNew(length: number = Infinity): ComposeMarks {
596
+ private dequeueNew(length: number = Number.POSITIVE_INFINITY): ComposeMarks {
597
597
  const newMark = this.newMarks.dequeueUpTo(length);
598
598
  const baseMark = createNoopMark(newMark.count, undefined, getInputCellId(newMark));
599
599
 
@@ -42,7 +42,7 @@ export class MarkListFactory {
42
42
  if (prev !== undefined && prev.type === mark.type) {
43
43
  const merged = tryMergeMarks(prev, mark);
44
44
  if (merged !== undefined) {
45
- this.list.splice(this.list.length - 1, 1, merged);
45
+ this.list.splice(-1, 1, merged);
46
46
  return;
47
47
  }
48
48
  }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/tree";
9
- export const pkgVersion = "2.10.0-305357";
9
+ export const pkgVersion = "2.10.0-306579";
@@ -297,7 +297,7 @@ export class SchematizingSimpleTreeView<
297
297
  new HydratedContext(this.rootFieldSchema.allowedTypeSet, view.context),
298
298
  );
299
299
 
300
- const unregister = this.checkout.storedSchema.on("afterSchemaChange", () => {
300
+ const unregister = this.checkout.storedSchema.events.on("afterSchemaChange", () => {
301
301
  unregister();
302
302
  this.unregisterCallbacks.delete(unregister);
303
303
  view[disposeSymbol]();
@@ -307,7 +307,7 @@ export class SchematizingSimpleTreeView<
307
307
  this.view = undefined;
308
308
  this.checkout.forest.anchors.slots.delete(SimpleContextSlot);
309
309
 
310
- const unregister = this.checkout.storedSchema.on("afterSchemaChange", () => {
310
+ const unregister = this.checkout.storedSchema.events.on("afterSchemaChange", () => {
311
311
  unregister();
312
312
  this.unregisterCallbacks.delete(unregister);
313
313
  this.update();
@@ -478,10 +478,10 @@ function runTransactionInCheckout<TResult>(
478
478
  let result: ReturnType<typeof transaction>;
479
479
  try {
480
480
  result = transaction();
481
- } catch (e) {
481
+ } catch (error) {
482
482
  // If the transaction has an unhandled error, abort and rollback the transaction but continue to propagate the error.
483
483
  checkout.transaction.abort();
484
- throw e;
484
+ throw error;
485
485
  }
486
486
 
487
487
  if (result === rollback) {
@@ -444,15 +444,15 @@ export class TreeCheckout implements ITreeCheckoutFork {
444
444
  private readonly breaker: Breakable = new Breakable("TreeCheckout"),
445
445
  ) {
446
446
  // when a transaction is started, take a snapshot of the current state of removed roots
447
- _branch.on("transactionStarted", () => {
447
+ _branch.events.on("transactionStarted", () => {
448
448
  this.removedRootsSnapshots.push(this.removedRoots.clone());
449
449
  });
450
450
  // when a transaction is committed, the latest snapshot of removed roots can be discarded
451
- _branch.on("transactionCommitted", () => {
451
+ _branch.events.on("transactionCommitted", () => {
452
452
  this.removedRootsSnapshots.pop();
453
453
  });
454
454
  // after a transaction is rolled back, revert removed roots back to the latest snapshot
455
- _branch.on("transactionRolledBack", () => {
455
+ _branch.events.on("transactionRolledBack", () => {
456
456
  const snapshot = this.removedRootsSnapshots.pop();
457
457
  assert(snapshot !== undefined, 0x9ae /* a snapshot for removed roots does not exist */);
458
458
  this.removedRoots = snapshot;
@@ -462,7 +462,7 @@ export class TreeCheckout implements ITreeCheckoutFork {
462
462
  // For example, a bug in the editor might produce a malformed change object and thus applying the change to the forest will throw an error.
463
463
  // In such a case we will crash here, preventing the change from being added to the commit graph, and preventing `afterChange` from firing.
464
464
  // One important consequence of this is that we will not submit the op containing the invalid change, since op submissions happens in response to `afterChange`.
465
- _branch.on("beforeChange", (event) => {
465
+ _branch.events.on("beforeChange", (event) => {
466
466
  if (event.change !== undefined) {
467
467
  const revision =
468
468
  event.type === "replace"
@@ -509,7 +509,7 @@ export class TreeCheckout implements ITreeCheckoutFork {
509
509
  }
510
510
  }
511
511
  });
512
- _branch.on("afterChange", (event) => {
512
+ _branch.events.on("afterChange", (event) => {
513
513
  // The following logic allows revertibles to be generated for the change.
514
514
  // Currently only appends (including merges) and transaction commits are supported.
515
515
  if (!_branch.isTransacting()) {
@@ -590,7 +590,7 @@ export class TreeCheckout implements ITreeCheckoutFork {
590
590
 
591
591
  // When the branch is trimmed, we can garbage collect any repair data whose latest relevant revision is one of the
592
592
  // trimmed revisions.
593
- _branch.on("ancestryTrimmed", (revisions) => {
593
+ _branch.events.on("ancestryTrimmed", (revisions) => {
594
594
  this.withCombinedVisitor((visitor) => {
595
595
  revisions.forEach((revision) => {
596
596
  // get all the roots last created or used by the revision
@@ -653,7 +653,7 @@ export class TreeCheckout implements ITreeCheckoutFork {
653
653
  }
654
654
 
655
655
  public get rootEvents(): Listenable<AnchorSetRootEvents> {
656
- return this.forest.anchors;
656
+ return this.forest.anchors.events;
657
657
  }
658
658
 
659
659
  public get editor(): ISharedTreeEditor {
@@ -21,7 +21,7 @@ import {
21
21
  tagRollbackInverse,
22
22
  type RebaseStatsWithDuration,
23
23
  } from "../core/index.js";
24
- import { EventEmitter, type Listenable } from "../events/index.js";
24
+ import { createEmitter, type Listenable } from "../events/index.js";
25
25
 
26
26
  import { TransactionStack } from "./transactionStack.js";
27
27
  import { fail } from "../util/index.js";
@@ -175,10 +175,9 @@ export interface BranchTrimmingEvents {
175
175
  /**
176
176
  * A branch of changes that can be applied to a SharedTree.
177
177
  */
178
- export class SharedTreeBranch<
179
- TEditor extends ChangeFamilyEditor,
180
- TChange,
181
- > extends EventEmitter<SharedTreeBranchEvents<TEditor, TChange>> {
178
+ export class SharedTreeBranch<TEditor extends ChangeFamilyEditor, TChange> {
179
+ readonly #events = createEmitter<SharedTreeBranchEvents<TEditor, TChange>>();
180
+ public readonly events: Listenable<SharedTreeBranchEvents<TEditor, TChange>> = this.#events;
182
181
  public readonly editor: TEditor;
183
182
  private readonly transactions = new TransactionStack();
184
183
  /**
@@ -222,12 +221,11 @@ export class SharedTreeBranch<
222
221
  keyof RebaseStatsWithDuration
223
222
  >,
224
223
  ) {
225
- super();
226
224
  this.editor = this.changeFamily.buildEditor(mintRevisionTag, (change) =>
227
225
  this.apply(change),
228
226
  );
229
227
  this.unsubscribeBranchTrimmer = branchTrimmer?.on("ancestryTrimmed", (commit) => {
230
- this.emit("ancestryTrimmed", commit);
228
+ this.#events.emit("ancestryTrimmed", commit);
231
229
  });
232
230
  }
233
231
 
@@ -267,9 +265,9 @@ export class SharedTreeBranch<
267
265
  newCommits: [newHead],
268
266
  } as const;
269
267
 
270
- this.emit("beforeChange", changeEvent);
268
+ this.#events.emit("beforeChange", changeEvent);
271
269
  this.head = newHead;
272
- this.emit("afterChange", changeEvent);
270
+ this.#events.emit("afterChange", changeEvent);
273
271
  return [taggedChange.change, newHead];
274
272
  }
275
273
 
@@ -290,7 +288,7 @@ export class SharedTreeBranch<
290
288
  const onDisposeUnSubscribes: (() => void)[] = [];
291
289
  const onForkUnSubscribe = onForkTransitive(this, (fork) => {
292
290
  forks.add(fork);
293
- onDisposeUnSubscribes.push(fork.on("dispose", () => forks.delete(fork)));
291
+ onDisposeUnSubscribes.push(fork.events.on("dispose", () => forks.delete(fork)));
294
292
  });
295
293
  this.transactions.push(this.head.revision, () => {
296
294
  forks.forEach((fork) => fork.dispose());
@@ -298,7 +296,7 @@ export class SharedTreeBranch<
298
296
  onForkUnSubscribe();
299
297
  });
300
298
  this.editor.enterTransaction();
301
- this.emit("transactionStarted", this.transactions.size === 1);
299
+ this.#events.emit("transactionStarted", this.transactions.size === 1);
302
300
  }
303
301
 
304
302
  /**
@@ -315,7 +313,7 @@ export class SharedTreeBranch<
315
313
  const [startCommit, commits] = this.popTransaction();
316
314
  this.editor.exitTransaction();
317
315
 
318
- this.emit("transactionCommitted", this.transactions.size === 0);
316
+ this.#events.emit("transactionCommitted", this.transactions.size === 0);
319
317
  if (commits.length === 0) {
320
318
  return undefined;
321
319
  }
@@ -336,9 +334,9 @@ export class SharedTreeBranch<
336
334
  newCommits: [newHead],
337
335
  } as const;
338
336
 
339
- this.emit("beforeChange", changeEvent);
337
+ this.#events.emit("beforeChange", changeEvent);
340
338
  this.head = newHead;
341
- this.emit("afterChange", changeEvent);
339
+ this.#events.emit("afterChange", changeEvent);
342
340
  return [commits, newHead];
343
341
  }
344
342
 
@@ -356,9 +354,9 @@ export class SharedTreeBranch<
356
354
  const [startCommit, commits] = this.popTransaction();
357
355
  this.editor.exitTransaction();
358
356
 
359
- this.emit("transactionAborted", this.transactions.size === 0);
357
+ this.#events.emit("transactionAborted", this.transactions.size === 0);
360
358
  if (commits.length === 0) {
361
- this.emit("transactionRolledBack", this.transactions.size === 0);
359
+ this.#events.emit("transactionRolledBack", this.transactions.size === 0);
362
360
  return [undefined, []];
363
361
  }
364
362
 
@@ -384,10 +382,10 @@ export class SharedTreeBranch<
384
382
  removedCommits: commits,
385
383
  } as const;
386
384
 
387
- this.emit("beforeChange", changeEvent);
385
+ this.#events.emit("beforeChange", changeEvent);
388
386
  this.head = startCommit;
389
- this.emit("afterChange", changeEvent);
390
- this.emit("transactionRolledBack", this.transactions.size === 0);
387
+ this.#events.emit("afterChange", changeEvent);
388
+ this.#events.emit("transactionRolledBack", this.transactions.size === 0);
391
389
  return [change, commits];
392
390
  }
393
391
 
@@ -435,7 +433,7 @@ export class SharedTreeBranch<
435
433
  this.mintRevisionTag,
436
434
  this.branchTrimmer,
437
435
  );
438
- this.emit("fork", fork);
436
+ this.#events.emit("fork", fork);
439
437
  return fork;
440
438
  }
441
439
 
@@ -483,9 +481,9 @@ export class SharedTreeBranch<
483
481
  newCommits,
484
482
  } as const;
485
483
 
486
- this.emit("beforeChange", changeEvent);
484
+ this.#events.emit("beforeChange", changeEvent);
487
485
  this.head = newSourceHead;
488
- this.emit("afterChange", changeEvent);
486
+ this.#events.emit("afterChange", changeEvent);
489
487
  return rebaseResult;
490
488
  }
491
489
 
@@ -529,9 +527,9 @@ export class SharedTreeBranch<
529
527
  newCommits: sourceCommits,
530
528
  } as const;
531
529
 
532
- this.emit("beforeChange", changeEvent);
530
+ this.#events.emit("beforeChange", changeEvent);
533
531
  this.head = rebaseResult.newSourceHead;
534
- this.emit("afterChange", changeEvent);
532
+ this.#events.emit("afterChange", changeEvent);
535
533
  return [change, sourceCommits];
536
534
  }
537
535
 
@@ -585,7 +583,7 @@ export class SharedTreeBranch<
585
583
  this.unsubscribeBranchTrimmer?.();
586
584
 
587
585
  this.disposed = true;
588
- this.emit("dispose");
586
+ this.#events.emit("dispose");
589
587
  }
590
588
 
591
589
  private assertNotDisposed(): void {
@@ -594,20 +592,22 @@ export class SharedTreeBranch<
594
592
  }
595
593
 
596
594
  /**
597
- * Registers an event listener that fires when the given forkable object forks.
595
+ * Registers an event listener that fires when the given branch forks.
598
596
  * The listener will also fire when any of those forks fork, and when those forks of forks fork, and so on.
599
- * @param forkable - an object that emits an event when it is forked
597
+ * @param branch - the branch that will be listened to for forks
600
598
  * @param onFork - the fork event listener
601
599
  * @returns a function which when called will deregister all registrations (including transitive) created by this function.
602
600
  * The deregister function has undefined behavior if called more than once.
603
601
  */
604
- export function onForkTransitive<T extends Listenable<{ fork: (t: T) => void }>>(
605
- forkable: T,
602
+ // Branches are invariant over TChange
603
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
604
+ export function onForkTransitive<T extends SharedTreeBranch<ChangeFamilyEditor, any>>(
605
+ branch: T,
606
606
  onFork: (fork: T) => void,
607
607
  ): () => void {
608
608
  const offs: (() => void)[] = [];
609
609
  offs.push(
610
- forkable.on("fork", (fork) => {
610
+ branch.events.on("fork", (fork: T) => {
611
611
  offs.push(onForkTransitive(fork, onFork));
612
612
  onFork(fork);
613
613
  }),
@@ -191,7 +191,7 @@ export class EditManager<
191
191
  this.telemetryEventBatcher,
192
192
  );
193
193
 
194
- this.localBranch.on("afterChange", (event) => {
194
+ this.localBranch.events.on("afterChange", (event) => {
195
195
  if (event.type === "append") {
196
196
  for (const commit of event.newCommits) {
197
197
  this.localCommits.push(commit);
@@ -223,19 +223,19 @@ export class EditManager<
223
223
  private registerBranch(branch: SharedTreeBranch<TEditor, TChangeset>): void {
224
224
  this.trackBranch(branch);
225
225
  // Whenever the branch is rebased, update our record of its base trunk commit
226
- const offBeforeRebase = branch.on("beforeChange", (args) => {
226
+ const offBeforeRebase = branch.events.on("beforeChange", (args) => {
227
227
  if (args.type === "replace" && getChangeReplaceType(args) === "rebase") {
228
228
  this.untrackBranch(branch);
229
229
  }
230
230
  });
231
- const offAfterRebase = branch.on("afterChange", (args) => {
231
+ const offAfterRebase = branch.events.on("afterChange", (args) => {
232
232
  if (args.type === "replace" && getChangeReplaceType(args) === "rebase") {
233
233
  this.trackBranch(branch);
234
234
  this.trimTrunk();
235
235
  }
236
236
  });
237
237
  // When the branch is disposed, update our branch set and trim the trunk
238
- const offDispose = branch.on("dispose", () => {
238
+ const offDispose = branch.events.on("dispose", () => {
239
239
  this.untrackBranch(branch);
240
240
  this.trimTrunk();
241
241
  offBeforeRebase();