@fluidframework/tree 2.10.0-304831 → 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 (296) 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/index.d.ts +1 -1
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +8 -1
  68. package/dist/index.js.map +1 -1
  69. package/dist/packageVersion.d.ts +1 -1
  70. package/dist/packageVersion.js +1 -1
  71. package/dist/packageVersion.js.map +1 -1
  72. package/dist/shared-tree/schematizingTreeView.js +2 -2
  73. package/dist/shared-tree/schematizingTreeView.js.map +1 -1
  74. package/dist/shared-tree/treeApi.js +2 -2
  75. package/dist/shared-tree/treeApi.js.map +1 -1
  76. package/dist/shared-tree/treeCheckout.js +7 -7
  77. package/dist/shared-tree/treeCheckout.js.map +1 -1
  78. package/dist/shared-tree-core/branch.d.ts +7 -7
  79. package/dist/shared-tree-core/branch.d.ts.map +1 -1
  80. package/dist/shared-tree-core/branch.js +35 -25
  81. package/dist/shared-tree-core/branch.js.map +1 -1
  82. package/dist/shared-tree-core/editManager.js +4 -4
  83. package/dist/shared-tree-core/editManager.js.map +1 -1
  84. package/dist/shared-tree-core/sharedTreeCore.js +5 -5
  85. package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
  86. package/dist/simple-tree/api/getSimpleSchema.d.ts +2 -0
  87. package/dist/simple-tree/api/getSimpleSchema.d.ts.map +1 -1
  88. package/dist/simple-tree/api/getSimpleSchema.js +2 -0
  89. package/dist/simple-tree/api/getSimpleSchema.js.map +1 -1
  90. package/dist/simple-tree/api/index.d.ts +1 -1
  91. package/dist/simple-tree/api/index.d.ts.map +1 -1
  92. package/dist/simple-tree/api/index.js.map +1 -1
  93. package/dist/simple-tree/api/schemaFactory.d.ts +66 -10
  94. package/dist/simple-tree/api/schemaFactory.d.ts.map +1 -1
  95. package/dist/simple-tree/api/schemaFactory.js +34 -9
  96. package/dist/simple-tree/api/schemaFactory.js.map +1 -1
  97. package/dist/simple-tree/api/simpleSchema.d.ts +12 -3
  98. package/dist/simple-tree/api/simpleSchema.d.ts.map +1 -1
  99. package/dist/simple-tree/api/simpleSchema.js.map +1 -1
  100. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js +2 -2
  101. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  102. package/dist/simple-tree/api/treeNodeApi.js +4 -4
  103. package/dist/simple-tree/api/treeNodeApi.js.map +1 -1
  104. package/dist/simple-tree/api/viewSchemaToSimpleSchema.js +5 -5
  105. package/dist/simple-tree/api/viewSchemaToSimpleSchema.js.map +1 -1
  106. package/dist/simple-tree/arrayNode.js +1 -1
  107. package/dist/simple-tree/arrayNode.js.map +1 -1
  108. package/dist/simple-tree/core/treeNodeKernel.d.ts +3 -3
  109. package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  110. package/dist/simple-tree/core/treeNodeKernel.js +7 -8
  111. package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
  112. package/dist/simple-tree/index.d.ts +2 -2
  113. package/dist/simple-tree/index.d.ts.map +1 -1
  114. package/dist/simple-tree/index.js +3 -2
  115. package/dist/simple-tree/index.js.map +1 -1
  116. package/dist/simple-tree/leafNodeSchema.d.ts +15 -0
  117. package/dist/simple-tree/leafNodeSchema.d.ts.map +1 -1
  118. package/dist/simple-tree/leafNodeSchema.js +15 -0
  119. package/dist/simple-tree/leafNodeSchema.js.map +1 -1
  120. package/dist/simple-tree/proxies.js +1 -1
  121. package/dist/simple-tree/proxies.js.map +1 -1
  122. package/dist/simple-tree/schemaTypes.d.ts +29 -1
  123. package/dist/simple-tree/schemaTypes.d.ts.map +1 -1
  124. package/dist/simple-tree/schemaTypes.js +3 -0
  125. package/dist/simple-tree/schemaTypes.js.map +1 -1
  126. package/dist/simple-tree/treeNodeValid.js +2 -2
  127. package/dist/simple-tree/treeNodeValid.js.map +1 -1
  128. package/dist/util/nestedMap.d.ts.map +1 -1
  129. package/dist/util/nestedMap.js.map +1 -1
  130. package/lib/core/forest/forest.d.ts +5 -1
  131. package/lib/core/forest/forest.d.ts.map +1 -1
  132. package/lib/core/forest/forest.js.map +1 -1
  133. package/lib/core/index.d.ts +1 -1
  134. package/lib/core/index.d.ts.map +1 -1
  135. package/lib/core/index.js.map +1 -1
  136. package/lib/core/schema-stored/storedSchemaRepository.d.ts +7 -3
  137. package/lib/core/schema-stored/storedSchemaRepository.d.ts.map +1 -1
  138. package/lib/core/schema-stored/storedSchemaRepository.js +4 -6
  139. package/lib/core/schema-stored/storedSchemaRepository.js.map +1 -1
  140. package/lib/core/tree/anchorSet.d.ts +8 -5
  141. package/lib/core/tree/anchorSet.d.ts.map +1 -1
  142. package/lib/core/tree/anchorSet.js +12 -11
  143. package/lib/core/tree/anchorSet.js.map +1 -1
  144. package/lib/events/emitter.d.ts +21 -9
  145. package/lib/events/emitter.d.ts.map +1 -1
  146. package/lib/events/emitter.js +37 -22
  147. package/lib/events/emitter.js.map +1 -1
  148. package/lib/events/listeners.d.ts +16 -5
  149. package/lib/events/listeners.d.ts.map +1 -1
  150. package/lib/events/listeners.js.map +1 -1
  151. package/lib/feature-libraries/chunked-forest/chunkTree.js +1 -1
  152. package/lib/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
  153. package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts +3 -2
  154. package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
  155. package/lib/feature-libraries/chunked-forest/chunkedForest.js +14 -9
  156. package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
  157. package/lib/feature-libraries/flex-tree/context.d.ts +3 -2
  158. package/lib/feature-libraries/flex-tree/context.d.ts.map +1 -1
  159. package/lib/feature-libraries/flex-tree/context.js +3 -3
  160. package/lib/feature-libraries/flex-tree/context.js.map +1 -1
  161. package/lib/feature-libraries/flex-tree/lazyField.js +1 -1
  162. package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
  163. package/lib/feature-libraries/flex-tree/lazyNode.js +1 -1
  164. package/lib/feature-libraries/flex-tree/lazyNode.js.map +1 -1
  165. package/lib/feature-libraries/flex-tree/utilities.js +1 -1
  166. package/lib/feature-libraries/flex-tree/utilities.js.map +1 -1
  167. package/lib/feature-libraries/modular-schema/comparison.d.ts.map +1 -1
  168. package/lib/feature-libraries/modular-schema/comparison.js +3 -0
  169. package/lib/feature-libraries/modular-schema/comparison.js.map +1 -1
  170. package/lib/feature-libraries/modular-schema/discrepancies.d.ts +2 -2
  171. package/lib/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -1
  172. package/lib/feature-libraries/modular-schema/discrepancies.js +91 -45
  173. package/lib/feature-libraries/modular-schema/discrepancies.js.map +1 -1
  174. package/lib/feature-libraries/modular-schema/genericFieldKind.js +2 -2
  175. package/lib/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  176. package/lib/feature-libraries/modular-schema/modularChangeFamily.js +1 -1
  177. package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  178. package/lib/feature-libraries/object-forest/objectForest.d.ts +2 -2
  179. package/lib/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
  180. package/lib/feature-libraries/object-forest/objectForest.js +6 -8
  181. package/lib/feature-libraries/object-forest/objectForest.js.map +1 -1
  182. package/lib/feature-libraries/schema-index/schemaSummarizer.js +1 -1
  183. package/lib/feature-libraries/schema-index/schemaSummarizer.js.map +1 -1
  184. package/lib/feature-libraries/sequence-field/compose.js +2 -2
  185. package/lib/feature-libraries/sequence-field/compose.js.map +1 -1
  186. package/lib/feature-libraries/sequence-field/markListFactory.js +1 -1
  187. package/lib/feature-libraries/sequence-field/markListFactory.js.map +1 -1
  188. package/lib/index.d.ts +1 -1
  189. package/lib/index.d.ts.map +1 -1
  190. package/lib/index.js +1 -1
  191. package/lib/index.js.map +1 -1
  192. package/lib/packageVersion.d.ts +1 -1
  193. package/lib/packageVersion.js +1 -1
  194. package/lib/packageVersion.js.map +1 -1
  195. package/lib/shared-tree/schematizingTreeView.js +2 -2
  196. package/lib/shared-tree/schematizingTreeView.js.map +1 -1
  197. package/lib/shared-tree/treeApi.js +2 -2
  198. package/lib/shared-tree/treeApi.js.map +1 -1
  199. package/lib/shared-tree/treeCheckout.js +7 -7
  200. package/lib/shared-tree/treeCheckout.js.map +1 -1
  201. package/lib/shared-tree-core/branch.d.ts +7 -7
  202. package/lib/shared-tree-core/branch.d.ts.map +1 -1
  203. package/lib/shared-tree-core/branch.js +36 -26
  204. package/lib/shared-tree-core/branch.js.map +1 -1
  205. package/lib/shared-tree-core/editManager.js +4 -4
  206. package/lib/shared-tree-core/editManager.js.map +1 -1
  207. package/lib/shared-tree-core/sharedTreeCore.js +5 -5
  208. package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
  209. package/lib/simple-tree/api/getSimpleSchema.d.ts +2 -0
  210. package/lib/simple-tree/api/getSimpleSchema.d.ts.map +1 -1
  211. package/lib/simple-tree/api/getSimpleSchema.js +2 -0
  212. package/lib/simple-tree/api/getSimpleSchema.js.map +1 -1
  213. package/lib/simple-tree/api/index.d.ts +1 -1
  214. package/lib/simple-tree/api/index.d.ts.map +1 -1
  215. package/lib/simple-tree/api/index.js.map +1 -1
  216. package/lib/simple-tree/api/schemaFactory.d.ts +66 -10
  217. package/lib/simple-tree/api/schemaFactory.d.ts.map +1 -1
  218. package/lib/simple-tree/api/schemaFactory.js +34 -9
  219. package/lib/simple-tree/api/schemaFactory.js.map +1 -1
  220. package/lib/simple-tree/api/simpleSchema.d.ts +12 -3
  221. package/lib/simple-tree/api/simpleSchema.d.ts.map +1 -1
  222. package/lib/simple-tree/api/simpleSchema.js.map +1 -1
  223. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js +2 -2
  224. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  225. package/lib/simple-tree/api/treeNodeApi.js +4 -4
  226. package/lib/simple-tree/api/treeNodeApi.js.map +1 -1
  227. package/lib/simple-tree/api/viewSchemaToSimpleSchema.js +5 -5
  228. package/lib/simple-tree/api/viewSchemaToSimpleSchema.js.map +1 -1
  229. package/lib/simple-tree/arrayNode.js +1 -1
  230. package/lib/simple-tree/arrayNode.js.map +1 -1
  231. package/lib/simple-tree/core/treeNodeKernel.d.ts +3 -3
  232. package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  233. package/lib/simple-tree/core/treeNodeKernel.js +7 -8
  234. package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
  235. package/lib/simple-tree/index.d.ts +2 -2
  236. package/lib/simple-tree/index.d.ts.map +1 -1
  237. package/lib/simple-tree/index.js +1 -1
  238. package/lib/simple-tree/index.js.map +1 -1
  239. package/lib/simple-tree/leafNodeSchema.d.ts +15 -0
  240. package/lib/simple-tree/leafNodeSchema.d.ts.map +1 -1
  241. package/lib/simple-tree/leafNodeSchema.js +15 -0
  242. package/lib/simple-tree/leafNodeSchema.js.map +1 -1
  243. package/lib/simple-tree/proxies.js +1 -1
  244. package/lib/simple-tree/proxies.js.map +1 -1
  245. package/lib/simple-tree/schemaTypes.d.ts +29 -1
  246. package/lib/simple-tree/schemaTypes.d.ts.map +1 -1
  247. package/lib/simple-tree/schemaTypes.js +3 -0
  248. package/lib/simple-tree/schemaTypes.js.map +1 -1
  249. package/lib/simple-tree/treeNodeValid.js +2 -2
  250. package/lib/simple-tree/treeNodeValid.js.map +1 -1
  251. package/lib/util/nestedMap.d.ts.map +1 -1
  252. package/lib/util/nestedMap.js.map +1 -1
  253. package/package.json +20 -20
  254. package/src/core/forest/forest.ts +6 -1
  255. package/src/core/index.ts +1 -1
  256. package/src/core/schema-stored/storedSchemaRepository.ts +10 -13
  257. package/src/core/tree/anchorSet.ts +13 -20
  258. package/src/events/emitter.ts +45 -24
  259. package/src/events/listeners.ts +17 -5
  260. package/src/feature-libraries/chunked-forest/chunkTree.ts +1 -1
  261. package/src/feature-libraries/chunked-forest/chunkedForest.ts +8 -14
  262. package/src/feature-libraries/flex-tree/context.ts +5 -7
  263. package/src/feature-libraries/flex-tree/lazyField.ts +1 -1
  264. package/src/feature-libraries/flex-tree/lazyNode.ts +1 -1
  265. package/src/feature-libraries/flex-tree/utilities.ts +1 -1
  266. package/src/feature-libraries/modular-schema/comparison.ts +4 -0
  267. package/src/feature-libraries/modular-schema/discrepancies.ts +116 -50
  268. package/src/feature-libraries/modular-schema/genericFieldKind.ts +2 -2
  269. package/src/feature-libraries/modular-schema/modularChangeFamily.ts +1 -1
  270. package/src/feature-libraries/object-forest/objectForest.ts +5 -11
  271. package/src/feature-libraries/schema-index/schemaSummarizer.ts +1 -1
  272. package/src/feature-libraries/sequence-field/compose.ts +2 -2
  273. package/src/feature-libraries/sequence-field/markListFactory.ts +1 -1
  274. package/src/index.ts +15 -0
  275. package/src/packageVersion.ts +1 -1
  276. package/src/shared-tree/schematizingTreeView.ts +2 -2
  277. package/src/shared-tree/treeApi.ts +2 -2
  278. package/src/shared-tree/treeCheckout.ts +7 -7
  279. package/src/shared-tree-core/branch.ts +30 -30
  280. package/src/shared-tree-core/editManager.ts +4 -4
  281. package/src/shared-tree-core/sharedTreeCore.ts +5 -5
  282. package/src/simple-tree/api/getSimpleSchema.ts +2 -0
  283. package/src/simple-tree/api/index.ts +10 -1
  284. package/src/simple-tree/api/schemaFactory.ts +37 -11
  285. package/src/simple-tree/api/simpleSchema.ts +12 -3
  286. package/src/simple-tree/api/simpleSchemaToJsonSchema.ts +2 -2
  287. package/src/simple-tree/api/treeNodeApi.ts +4 -4
  288. package/src/simple-tree/api/viewSchemaToSimpleSchema.ts +5 -5
  289. package/src/simple-tree/arrayNode.ts +1 -1
  290. package/src/simple-tree/core/treeNodeKernel.ts +8 -10
  291. package/src/simple-tree/index.ts +8 -0
  292. package/src/simple-tree/leafNodeSchema.ts +19 -0
  293. package/src/simple-tree/proxies.ts +1 -1
  294. package/src/simple-tree/schemaTypes.ts +29 -1
  295. package/src/simple-tree/treeNodeValid.ts +2 -2
  296. package/src/util/nestedMap.ts +1 -0
@@ -206,7 +206,12 @@ export interface AnchorSetRootEvents {
206
206
  /**
207
207
  * Node in a tree of anchors.
208
208
  */
209
- export interface AnchorNode extends UpPath<AnchorNode>, Listenable<AnchorEvents> {
209
+ export interface AnchorNode extends UpPath<AnchorNode> {
210
+ /**
211
+ * Events for this anchor node.
212
+ */
213
+ readonly events: Listenable<AnchorEvents>;
214
+
210
215
  /**
211
216
  * Allows access to data stored on the Anchor in "slots".
212
217
  * Use {@link anchorSlot} to create slots.
@@ -277,8 +282,10 @@ export function anchorSlot<TContent>(): AnchorSlot<TContent> {
277
282
  *
278
283
  * @sealed
279
284
  */
280
- export class AnchorSet implements Listenable<AnchorSetRootEvents>, AnchorLocator {
281
- private readonly events = createEmitter<AnchorSetRootEvents>();
285
+ export class AnchorSet implements AnchorLocator {
286
+ readonly #events = createEmitter<AnchorSetRootEvents>();
287
+ public readonly events: Listenable<AnchorSetRootEvents> = this.#events;
288
+
282
289
  /**
283
290
  * Incrementing counter to give each anchor in this set a unique index for its identifier.
284
291
  * "0" is reserved for the `NeverAnchor`.
@@ -312,7 +319,7 @@ export class AnchorSet implements Listenable<AnchorSetRootEvents>, AnchorLocator
312
319
  private activeVisitor?: DeltaVisitor;
313
320
 
314
321
  public constructor() {
315
- this.on("treeChanging", () => {
322
+ this.events.on("treeChanging", () => {
316
323
  this.generationNumber += 1;
317
324
  });
318
325
  }
@@ -344,13 +351,6 @@ export class AnchorSet implements Listenable<AnchorSetRootEvents>, AnchorLocator
344
351
  }
345
352
  }
346
353
 
347
- public on<K extends keyof AnchorSetRootEvents>(
348
- eventName: K,
349
- listener: AnchorSetRootEvents[K],
350
- ): () => void {
351
- return this.events.on(eventName, listener);
352
- }
353
-
354
354
  /**
355
355
  * Check if there are currently no anchors tracked.
356
356
  * Mainly for testing anchor cleanup.
@@ -792,7 +792,7 @@ export class AnchorSet implements Listenable<AnchorSetRootEvents>, AnchorLocator
792
792
  notifyChildrenChanging(): void {
793
793
  this.maybeWithNode(
794
794
  (p) => p.events.emit("childrenChanging", p),
795
- () => this.anchorSet.events.emit("childrenChanging", this.anchorSet),
795
+ () => this.anchorSet.#events.emit("childrenChanging", this.anchorSet),
796
796
  );
797
797
  },
798
798
  notifyChildrenChanged(): void {
@@ -1098,7 +1098,7 @@ export class AnchorSet implements Listenable<AnchorSetRootEvents>, AnchorLocator
1098
1098
  this.parentField = undefined;
1099
1099
  },
1100
1100
  };
1101
- this.events.emit("treeChanging", this);
1101
+ this.#events.emit("treeChanging", this);
1102
1102
  this.activeVisitor = visitor;
1103
1103
  return visitor;
1104
1104
  }
@@ -1209,13 +1209,6 @@ class PathNode extends ReferenceCountedBase implements UpPath<PathNode>, AnchorN
1209
1209
  super(1);
1210
1210
  }
1211
1211
 
1212
- public on<K extends keyof AnchorEvents>(
1213
- eventName: K,
1214
- listener: AnchorEvents[K],
1215
- ): () => void {
1216
- return this.events.on(eventName, listener);
1217
- }
1218
-
1219
1212
  public child(key: FieldKey, index: number): UpPath<AnchorNode> {
1220
1213
  // Fast path: if child exists, return it.
1221
1214
  return (
@@ -3,7 +3,8 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { setInNestedMap } from "../util/index.js";
6
+ import { UsageError } from "@fluidframework/telemetry-utils/internal";
7
+ import { getOrCreate } from "../util/index.js";
7
8
  import type { Listenable, Listeners, Off } from "./listeners.js";
8
9
 
9
10
  /**
@@ -63,7 +64,7 @@ export interface HasListeners<TListeners extends Listeners<TListeners>> {
63
64
  /**
64
65
  * Provides an API for subscribing to and listening to events.
65
66
  *
66
- * @remarks Classes wishing to emit events may either extend this class or compose over it.
67
+ * @remarks Classes wishing to emit events may either extend this class, compose over it, or expose it as a property of type {@link Listenable}.
67
68
  *
68
69
  * @example Extending this class
69
70
  *
@@ -97,13 +98,27 @@ export interface HasListeners<TListeners extends Listeners<TListeners>> {
97
98
  * }
98
99
  * }
99
100
  * ```
101
+ *
102
+ * @example Exposing this class as a property
103
+ *
104
+ * ```typescript
105
+ * class MyExposingClass {
106
+ * private readonly _events = createEmitter<MyEvents>();
107
+ * public readonly events: Listenable<MyEvents> = this._events;
108
+ *
109
+ * private load() {
110
+ * this._events.emit("loaded");
111
+ * const results: number[] = this._events.emitAndCollect("computed");
112
+ * }
113
+ * }
114
+ * ```
100
115
  */
101
116
  export class EventEmitter<TListeners extends Listeners<TListeners>>
102
117
  implements Listenable<TListeners>, HasListeners<TListeners>
103
118
  {
104
119
  protected readonly listeners = new Map<
105
120
  keyof TListeners,
106
- Map<Off, (...args: any[]) => TListeners[keyof TListeners]>
121
+ Set<(...args: any[]) => TListeners[keyof TListeners]>
107
122
  >();
108
123
 
109
124
  // Because this is protected and not public, calling this externally (not from a subclass) makes sending events to the constructed instance impossible.
@@ -118,11 +133,10 @@ export class EventEmitter<TListeners extends Listeners<TListeners>>
118
133
  if (listeners !== undefined) {
119
134
  // Current tsc (5.4.5) cannot spread `args` into `listener()`.
120
135
  const argArray: unknown[] = args;
121
-
122
136
  // This explicitly copies listeners so that new listeners added during this call to emit will not receive this event.
123
- for (const [off, listener] of [...listeners]) {
137
+ for (const listener of [...listeners]) {
124
138
  // If listener has been unsubscribed while invoking other listeners, skip it.
125
- if (listeners.has(off)) {
139
+ if (listeners.has(listener)) {
126
140
  listener(...argArray);
127
141
  }
128
142
  }
@@ -145,29 +159,32 @@ export class EventEmitter<TListeners extends Listeners<TListeners>>
145
159
  return [];
146
160
  }
147
161
 
148
- /**
149
- * Register an event listener.
150
- * @param eventName - the name of the event
151
- * @param listener - the handler to run when the event is fired by the emitter
152
- * @returns a function which will deregister the listener when run.
153
- * This function will error if called more than once.
154
- */
155
162
  public on<K extends keyof Listeners<TListeners>>(
156
163
  eventName: K,
157
164
  listener: TListeners[K],
158
165
  ): Off {
159
- const off: Off = () => {
160
- const currentListeners = this.listeners.get(eventName);
161
- if (currentListeners?.delete(off) === true) {
162
- if (currentListeners.size === 0) {
163
- this.listeners.delete(eventName);
164
- this.noListeners?.(eventName);
165
- }
166
- }
167
- };
166
+ const listeners = getOrCreate(this.listeners, eventName, () => new Set());
167
+ if (listeners.has(listener)) {
168
+ const eventDescription =
169
+ typeof eventName === "symbol" ? eventName.description : String(eventName.toString());
168
170
 
169
- setInNestedMap(this.listeners, eventName, off, listener);
170
- return off;
171
+ throw new UsageError(
172
+ `Attempted to register the same listener object twice for event ${eventDescription}`,
173
+ );
174
+ }
175
+ listeners.add(listener);
176
+ return () => this.off(eventName, listener);
177
+ }
178
+
179
+ public off<K extends keyof Listeners<TListeners>>(
180
+ eventName: K,
181
+ listener: TListeners[K],
182
+ ): void {
183
+ const listeners = this.listeners.get(eventName);
184
+ if (listeners?.delete(listener) === true && listeners.size === 0) {
185
+ this.listeners.delete(eventName);
186
+ this.noListeners?.(eventName);
187
+ }
171
188
  }
172
189
 
173
190
  public hasListeners(eventName?: keyof TListeners): boolean {
@@ -225,6 +242,10 @@ class ComposableEventEmitter<TListeners extends Listeners<TListeners>>
225
242
  * public on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): Off {
226
243
  * return this.events.on(eventName, listener);
227
244
  * }
245
+ *
246
+ * public off<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): void {
247
+ * return this.events.off(eventName, listener);
248
+ * }
228
249
  * }
229
250
  * ```
230
251
  */
@@ -50,13 +50,25 @@ export interface Listenable<TListeners extends object> {
50
50
  /**
51
51
  * Register an event listener.
52
52
  * @param eventName - The name of the event.
53
- * @param listener - the handler to run when the event is fired by the emitter
54
- * @returns a {@link Off | function} which will deregister the listener when called.
55
- * This deregistration function is idempotent and therefore may be safely called more than once with no effect.
56
- * @remarks Do not register the exact same `listener` object for the same event more than once.
57
- * Doing so will result in undefined behavior, and is not guaranteed to behave the same in future versions of this library.
53
+ * @param listener - The listener function to run when the event is fired.
54
+ * @returns A {@link Off | function} which will deregister the listener when called.
55
+ * Calling the deregistration function more than once will have no effect.
56
+ *
57
+ * Listeners may also be deregistered by passing the listener to {@link Listenable.off | off()}.
58
+ * @remarks Registering the exact same `listener` object for the same event more than once will throw an error.
59
+ * If registering the same listener for the same event multiple times is desired, consider using a wrapper function for the second subscription.
58
60
  */
59
61
  on<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): Off;
62
+
63
+ /**
64
+ * Deregister an event listener.
65
+ * @param eventName - The name of the event.
66
+ * @param listener - The listener function to remove from the current set of event listeners.
67
+ * @remarks If `listener` is not currently registered, this method will have no effect.
68
+ *
69
+ * Listeners may also be deregistered by calling the {@link Off | deregistration function} returned when they are {@link Listenable.on | registered}.
70
+ */
71
+ off<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): void;
60
72
  }
61
73
 
62
74
  /**
@@ -134,7 +134,7 @@ export class Chunker implements IChunker {
134
134
  if (cached !== undefined) {
135
135
  return cached;
136
136
  }
137
- this.unregisterSchemaCallback = this.schema.on("afterSchemaChange", () =>
137
+ this.unregisterSchemaCallback = this.schema.events.on("afterSchemaChange", () =>
138
138
  this.schemaChanged(),
139
139
  );
140
140
  return this.tryShapeFromSchema(this.schema, this.policy, schema, this.typeShapes);
@@ -28,7 +28,7 @@ import {
28
28
  mapCursorField,
29
29
  rootFieldKey,
30
30
  } from "../../core/index.js";
31
- import { createEmitter } from "../../events/index.js";
31
+ import { createEmitter, type Listenable } from "../../events/index.js";
32
32
  import { assertValidRange, brand, fail, getOrAddEmptyToMap } from "../../util/index.js";
33
33
 
34
34
  import { BasicChunk, BasicChunkCursor, type SiblingsOrKey } from "./basicChunk.js";
@@ -53,7 +53,8 @@ interface StackNode {
53
53
  export class ChunkedForest implements IEditableForest {
54
54
  private activeVisitor?: DeltaVisitor;
55
55
 
56
- private readonly events = createEmitter<ForestEvents>();
56
+ readonly #events = createEmitter<ForestEvents>();
57
+ public readonly events: Listenable<ForestEvents> = this.#events;
57
58
 
58
59
  /**
59
60
  * @param roots - dummy node above the root under which detached fields are stored. All content of the forest is reachable from this.
@@ -73,13 +74,6 @@ export class ChunkedForest implements IEditableForest {
73
74
  return this.roots.fields.size === 0;
74
75
  }
75
76
 
76
- public on<K extends keyof ForestEvents>(
77
- eventName: K,
78
- listener: ForestEvents[K],
79
- ): () => void {
80
- return this.events.on(eventName, listener);
81
- }
82
-
83
77
  public clone(schema: TreeStoredSchemaSubscription, anchors: AnchorSet): ChunkedForest {
84
78
  this.roots.referenceAdded();
85
79
  return new ChunkedForest(this.roots, schema, this.chunker.clone(schema), anchors);
@@ -119,11 +113,11 @@ export class ChunkedForest implements IEditableForest {
119
113
  this.forest.activeVisitor = undefined;
120
114
  },
121
115
  destroy(detachedField: FieldKey, count: number): void {
122
- this.forest.events.emit("beforeChange");
116
+ this.forest.#events.emit("beforeChange");
123
117
  this.forest.roots.fields.delete(detachedField);
124
118
  },
125
119
  create(content: ProtoNodes, destination: FieldKey): void {
126
- this.forest.events.emit("beforeChange");
120
+ this.forest.#events.emit("beforeChange");
127
121
  const chunks: TreeChunk[] = content.map((c) =>
128
122
  chunkTree(c, {
129
123
  policy: this.forest.chunker,
@@ -131,7 +125,7 @@ export class ChunkedForest implements IEditableForest {
131
125
  }),
132
126
  );
133
127
  this.forest.roots.fields.set(destination, chunks);
134
- this.forest.events.emit("afterRootFieldCreated", destination);
128
+ this.forest.#events.emit("afterRootFieldCreated", destination);
135
129
  },
136
130
  attach(source: FieldKey, count: number, destination: PlaceIndex): void {
137
131
  this.attachEdit(source, count, destination);
@@ -146,7 +140,7 @@ export class ChunkedForest implements IEditableForest {
146
140
  * @param destination - The index in the current field at which to attach the content.
147
141
  */
148
142
  attachEdit(source: FieldKey, count: number, destination: PlaceIndex): void {
149
- this.forest.events.emit("beforeChange");
143
+ this.forest.#events.emit("beforeChange");
150
144
  const sourceField = this.forest.roots.fields.get(source) ?? [];
151
145
  this.forest.roots.fields.delete(source);
152
146
  if (sourceField.length === 0) {
@@ -166,7 +160,7 @@ export class ChunkedForest implements IEditableForest {
166
160
  * If not specified, the detached range is destroyed.
167
161
  */
168
162
  detachEdit(source: Range, destination: FieldKey | undefined): void {
169
- this.forest.events.emit("beforeChange");
163
+ this.forest.#events.emit("beforeChange");
170
164
  const parent = this.getParent();
171
165
  const sourceField = parent.mutableChunk.fields.get(parent.key) ?? [];
172
166
 
@@ -49,7 +49,8 @@ export interface FlexTreeContext {
49
49
  * A common context of a "forest" of FlexTrees.
50
50
  * It handles group operations like transforming cursors into anchors for edits.
51
51
  */
52
- export interface FlexTreeHydratedContext extends FlexTreeContext, Listenable<ForestEvents> {
52
+ export interface FlexTreeHydratedContext extends FlexTreeContext {
53
+ readonly events: Listenable<ForestEvents>;
53
54
  /**
54
55
  * Gets the root field of the tree.
55
56
  */
@@ -92,7 +93,7 @@ export class Context implements FlexTreeHydratedContext, IDisposable {
92
93
  public readonly nodeKeyManager: NodeKeyManager,
93
94
  ) {
94
95
  this.eventUnregister = [
95
- this.checkout.forest.on("beforeChange", () => {
96
+ this.checkout.forest.events.on("beforeChange", () => {
96
97
  this.prepareForEdit();
97
98
  }),
98
99
  ];
@@ -160,11 +161,8 @@ export class Context implements FlexTreeHydratedContext, IDisposable {
160
161
  return field;
161
162
  }
162
163
 
163
- public on<K extends keyof ForestEvents>(
164
- eventName: K,
165
- listener: ForestEvents[K],
166
- ): () => void {
167
- return this.checkout.forest.on(eventName, listener);
164
+ public get events(): Listenable<ForestEvents> {
165
+ return this.checkout.forest.events;
168
166
  }
169
167
  }
170
168
 
@@ -151,7 +151,7 @@ export abstract class LazyField extends LazyEntity<FieldAnchor> implements FlexT
151
151
  const anchorNode =
152
152
  context.checkout.forest.anchors.locate(fieldAnchor.parent) ??
153
153
  fail("parent anchor node should always exist since field is under a node");
154
- this.offAfterDestroy = anchorNode.on("afterDestroy", () => {
154
+ this.offAfterDestroy = anchorNode.events.on("afterDestroy", () => {
155
155
  this[disposeSymbol]();
156
156
  });
157
157
  }
@@ -89,7 +89,7 @@ export class LazyTreeNode extends LazyEntity<Anchor> implements FlexTreeNode {
89
89
  this.storedSchema = context.schema.nodeSchema.get(this.schema) ?? fail("missing schema");
90
90
  assert(cursor.mode === CursorLocationType.Nodes, 0x783 /* must be in nodes mode */);
91
91
  anchorNode.slots.set(flexTreeSlot, this);
92
- this.#removeDeleteCallback = anchorNode.on("afterDestroy", cleanupTree);
92
+ this.#removeDeleteCallback = anchorNode.events.on("afterDestroy", cleanupTree);
93
93
  }
94
94
 
95
95
  public borrowCursor(): ITreeCursorSynchronous {
@@ -99,7 +99,7 @@ export function getSchemaAndPolicy(nodeOrField: FlexTreeEntity): SchemaAndPolicy
99
99
  */
100
100
  export function indexForAt(index: number, length: number): number | undefined {
101
101
  let finalIndex = Math.trunc(+index);
102
- if (isNaN(finalIndex)) {
102
+ if (Number.isNaN(finalIndex)) {
103
103
  finalIndex = 0;
104
104
  }
105
105
  if (finalIndex < -length || finalIndex >= length) {
@@ -54,6 +54,10 @@ export function allowsTreeSuperset(
54
54
  return false;
55
55
  }
56
56
 
57
+ if (superset instanceof LeafNodeStoredSchema) {
58
+ return false;
59
+ }
60
+
57
61
  assert(
58
62
  original instanceof MapNodeStoredSchema || original instanceof ObjectNodeStoredSchema,
59
63
  0x893 /* unsupported node kind */,
@@ -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
  }