@fluidframework/tree 2.110.0 → 2.111.0

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 (374) hide show
  1. package/.vscode/settings.json +1 -0
  2. package/CHANGELOG.md +48 -0
  3. package/api-report/tree.alpha.api.md +48 -31
  4. package/api-report/tree.beta.api.md +38 -0
  5. package/api-report/tree.legacy.beta.api.md +38 -0
  6. package/dist/core/change-family/changeFamily.d.ts +3 -10
  7. package/dist/core/change-family/changeFamily.d.ts.map +1 -1
  8. package/dist/core/change-family/changeFamily.js.map +1 -1
  9. package/dist/core/rebase/types.d.ts +2 -2
  10. package/dist/core/rebase/types.js.map +1 -1
  11. package/dist/core/schema-stored/storedSchemaRepository.d.ts +2 -2
  12. package/dist/core/schema-stored/storedSchemaRepository.js.map +1 -1
  13. package/dist/entrypoints/alpha.d.ts +1 -1
  14. package/dist/entrypoints/alpha.d.ts.map +1 -1
  15. package/dist/entrypoints/alpha.js.map +1 -1
  16. package/dist/entrypoints/beta.d.ts +1 -1
  17. package/dist/entrypoints/beta.d.ts.map +1 -1
  18. package/dist/entrypoints/beta.js.map +1 -1
  19. package/dist/entrypoints/legacy.d.ts +1 -1
  20. package/dist/entrypoints/legacy.d.ts.map +1 -1
  21. package/dist/entrypoints/legacy.js.map +1 -1
  22. package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts +19 -14
  23. package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts.map +1 -1
  24. package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.js +13 -32
  25. package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.js.map +1 -1
  26. package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts +85 -28
  27. package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
  28. package/dist/feature-libraries/chunked-forest/codec/codecs.js +83 -9
  29. package/dist/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
  30. package/dist/feature-libraries/chunked-forest/codec/compressedEncode.d.ts +4 -2
  31. package/dist/feature-libraries/chunked-forest/codec/compressedEncode.d.ts.map +1 -1
  32. package/dist/feature-libraries/chunked-forest/codec/compressedEncode.js +11 -0
  33. package/dist/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
  34. package/dist/feature-libraries/chunked-forest/codec/index.d.ts +1 -1
  35. package/dist/feature-libraries/chunked-forest/codec/index.d.ts.map +1 -1
  36. package/dist/feature-libraries/chunked-forest/codec/index.js +2 -1
  37. package/dist/feature-libraries/chunked-forest/codec/index.js.map +1 -1
  38. package/dist/feature-libraries/chunked-forest/codec/nodeEncoder.d.ts +1 -1
  39. package/dist/feature-libraries/chunked-forest/codec/nodeEncoder.d.ts.map +1 -1
  40. package/dist/feature-libraries/chunked-forest/codec/nodeEncoder.js +3 -15
  41. package/dist/feature-libraries/chunked-forest/codec/nodeEncoder.js.map +1 -1
  42. package/dist/feature-libraries/chunked-forest/index.d.ts +1 -1
  43. package/dist/feature-libraries/chunked-forest/index.d.ts.map +1 -1
  44. package/dist/feature-libraries/chunked-forest/index.js +2 -1
  45. package/dist/feature-libraries/chunked-forest/index.js.map +1 -1
  46. package/dist/feature-libraries/forest-summary/codec.d.ts +3 -3
  47. package/dist/feature-libraries/forest-summary/codec.d.ts.map +1 -1
  48. package/dist/feature-libraries/forest-summary/codec.js.map +1 -1
  49. package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts +3 -2
  50. package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
  51. package/dist/feature-libraries/forest-summary/forestSummarizer.js +4 -6
  52. package/dist/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
  53. package/dist/feature-libraries/index.d.ts +1 -1
  54. package/dist/feature-libraries/index.d.ts.map +1 -1
  55. package/dist/feature-libraries/index.js +2 -1
  56. package/dist/feature-libraries/index.js.map +1 -1
  57. package/dist/feature-libraries/modular-schema/modularChangeCodecV1.d.ts.map +1 -1
  58. package/dist/feature-libraries/modular-schema/modularChangeCodecV1.js +9 -9
  59. package/dist/feature-libraries/modular-schema/modularChangeCodecV1.js.map +1 -1
  60. package/dist/index.d.ts +2 -2
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +2 -1
  63. package/dist/index.js.map +1 -1
  64. package/dist/packageVersion.d.ts +1 -1
  65. package/dist/packageVersion.js +1 -1
  66. package/dist/packageVersion.js.map +1 -1
  67. package/dist/shared-tree/independentView.d.ts.map +1 -1
  68. package/dist/shared-tree/independentView.js +4 -7
  69. package/dist/shared-tree/independentView.js.map +1 -1
  70. package/dist/shared-tree/index.d.ts +1 -1
  71. package/dist/shared-tree/index.d.ts.map +1 -1
  72. package/dist/shared-tree/index.js.map +1 -1
  73. package/dist/shared-tree/schematizingTreeView.d.ts +5 -5
  74. package/dist/shared-tree/schematizingTreeView.d.ts.map +1 -1
  75. package/dist/shared-tree/schematizingTreeView.js.map +1 -1
  76. package/dist/shared-tree/sharedTree.d.ts +5 -0
  77. package/dist/shared-tree/sharedTree.d.ts.map +1 -1
  78. package/dist/shared-tree/sharedTree.js +7 -4
  79. package/dist/shared-tree/sharedTree.js.map +1 -1
  80. package/dist/shared-tree/transactionPostProcessor.d.ts +28 -0
  81. package/dist/shared-tree/transactionPostProcessor.d.ts.map +1 -0
  82. package/dist/shared-tree/transactionPostProcessor.js +28 -0
  83. package/dist/shared-tree/transactionPostProcessor.js.map +1 -0
  84. package/dist/shared-tree/treeAlpha.js +3 -3
  85. package/dist/shared-tree/treeAlpha.js.map +1 -1
  86. package/dist/shared-tree/treeCheckout.d.ts +16 -12
  87. package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
  88. package/dist/shared-tree/treeCheckout.js +18 -9
  89. package/dist/shared-tree/treeCheckout.js.map +1 -1
  90. package/dist/shared-tree/unhydratedTreeContext.d.ts +5 -5
  91. package/dist/shared-tree/unhydratedTreeContext.d.ts.map +1 -1
  92. package/dist/shared-tree/unhydratedTreeContext.js.map +1 -1
  93. package/dist/shared-tree-core/editManagerCodecs.d.ts +3 -2
  94. package/dist/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
  95. package/dist/shared-tree-core/editManagerCodecs.js.map +1 -1
  96. package/dist/shared-tree-core/editManagerCodecsCommons.d.ts +3 -9
  97. package/dist/shared-tree-core/editManagerCodecsCommons.d.ts.map +1 -1
  98. package/dist/shared-tree-core/editManagerCodecsCommons.js +2 -4
  99. package/dist/shared-tree-core/editManagerCodecsCommons.js.map +1 -1
  100. package/dist/shared-tree-core/editManagerCodecsV1toV4.d.ts +3 -3
  101. package/dist/shared-tree-core/editManagerCodecsV1toV4.d.ts.map +1 -1
  102. package/dist/shared-tree-core/editManagerCodecsV1toV4.js.map +1 -1
  103. package/dist/shared-tree-core/editManagerCodecsVSharedBranches.d.ts +3 -3
  104. package/dist/shared-tree-core/editManagerCodecsVSharedBranches.d.ts.map +1 -1
  105. package/dist/shared-tree-core/editManagerCodecsVSharedBranches.js.map +1 -1
  106. package/dist/shared-tree-core/editManagerSummarizer.d.ts +5 -9
  107. package/dist/shared-tree-core/editManagerSummarizer.d.ts.map +1 -1
  108. package/dist/shared-tree-core/editManagerSummarizer.js +5 -10
  109. package/dist/shared-tree-core/editManagerSummarizer.js.map +1 -1
  110. package/dist/shared-tree-core/index.d.ts +1 -1
  111. package/dist/shared-tree-core/index.d.ts.map +1 -1
  112. package/dist/shared-tree-core/index.js +3 -2
  113. package/dist/shared-tree-core/index.js.map +1 -1
  114. package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  115. package/dist/shared-tree-core/sharedTreeCore.js +3 -1
  116. package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
  117. package/dist/shared-tree-core/transaction.d.ts +84 -9
  118. package/dist/shared-tree-core/transaction.d.ts.map +1 -1
  119. package/dist/shared-tree-core/transaction.js +74 -7
  120. package/dist/shared-tree-core/transaction.js.map +1 -1
  121. package/dist/simple-tree/api/index.d.ts +1 -1
  122. package/dist/simple-tree/api/index.d.ts.map +1 -1
  123. package/dist/simple-tree/api/index.js.map +1 -1
  124. package/dist/simple-tree/api/schemaCreationUtilities.d.ts +3 -3
  125. package/dist/simple-tree/api/schemaCreationUtilities.js +3 -3
  126. package/dist/simple-tree/api/schemaCreationUtilities.js.map +1 -1
  127. package/dist/simple-tree/api/schemaFactory.d.ts +1 -1
  128. package/dist/simple-tree/api/schemaFactory.js +2 -2
  129. package/dist/simple-tree/api/schemaFactory.js.map +1 -1
  130. package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts +2 -2
  131. package/dist/simple-tree/api/snapshotCompatibilityChecker.js +2 -2
  132. package/dist/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
  133. package/dist/simple-tree/api/transactionTypes.d.ts +91 -28
  134. package/dist/simple-tree/api/transactionTypes.d.ts.map +1 -1
  135. package/dist/simple-tree/api/transactionTypes.js.map +1 -1
  136. package/dist/simple-tree/api/tree.d.ts +86 -22
  137. package/dist/simple-tree/api/tree.d.ts.map +1 -1
  138. package/dist/simple-tree/api/tree.js.map +1 -1
  139. package/dist/simple-tree/api/treeBeta.d.ts.map +1 -1
  140. package/dist/simple-tree/api/treeBeta.js.map +1 -1
  141. package/dist/simple-tree/core/treeNodeSchema.d.ts +7 -7
  142. package/dist/simple-tree/core/treeNodeSchema.js.map +1 -1
  143. package/dist/simple-tree/index.d.ts +1 -1
  144. package/dist/simple-tree/index.d.ts.map +1 -1
  145. package/dist/simple-tree/index.js.map +1 -1
  146. package/dist/text/index.d.ts +1 -0
  147. package/dist/text/index.d.ts.map +1 -1
  148. package/dist/text/index.js +3 -1
  149. package/dist/text/index.js.map +1 -1
  150. package/dist/text/textDomainFormatted.d.ts +69 -758
  151. package/dist/text/textDomainFormatted.d.ts.map +1 -1
  152. package/dist/text/textDomainFormatted.js +275 -322
  153. package/dist/text/textDomainFormatted.js.map +1 -1
  154. package/dist/text/textDomainFormattedDefault.d.ts +273 -0
  155. package/dist/text/textDomainFormattedDefault.d.ts.map +1 -0
  156. package/dist/text/textDomainFormattedDefault.js +94 -0
  157. package/dist/text/textDomainFormattedDefault.js.map +1 -0
  158. package/dist/util/compressedIds.d.ts +118 -0
  159. package/dist/util/compressedIds.d.ts.map +1 -0
  160. package/dist/util/compressedIds.js +133 -0
  161. package/dist/util/compressedIds.js.map +1 -0
  162. package/dist/util/index.d.ts +1 -0
  163. package/dist/util/index.d.ts.map +1 -1
  164. package/dist/util/index.js +9 -1
  165. package/dist/util/index.js.map +1 -1
  166. package/lib/core/change-family/changeFamily.d.ts +3 -10
  167. package/lib/core/change-family/changeFamily.d.ts.map +1 -1
  168. package/lib/core/change-family/changeFamily.js.map +1 -1
  169. package/lib/core/rebase/types.d.ts +2 -2
  170. package/lib/core/rebase/types.js.map +1 -1
  171. package/lib/core/schema-stored/storedSchemaRepository.d.ts +2 -2
  172. package/lib/core/schema-stored/storedSchemaRepository.js.map +1 -1
  173. package/lib/entrypoints/alpha.d.ts +1 -1
  174. package/lib/entrypoints/alpha.d.ts.map +1 -1
  175. package/lib/entrypoints/alpha.js.map +1 -1
  176. package/lib/entrypoints/beta.d.ts +1 -1
  177. package/lib/entrypoints/beta.d.ts.map +1 -1
  178. package/lib/entrypoints/beta.js.map +1 -1
  179. package/lib/entrypoints/legacy.d.ts +1 -1
  180. package/lib/entrypoints/legacy.d.ts.map +1 -1
  181. package/lib/entrypoints/legacy.js.map +1 -1
  182. package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts +19 -14
  183. package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts.map +1 -1
  184. package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.js +14 -33
  185. package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.js.map +1 -1
  186. package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts +85 -28
  187. package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
  188. package/lib/feature-libraries/chunked-forest/codec/codecs.js +82 -9
  189. package/lib/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
  190. package/lib/feature-libraries/chunked-forest/codec/compressedEncode.d.ts +4 -2
  191. package/lib/feature-libraries/chunked-forest/codec/compressedEncode.d.ts.map +1 -1
  192. package/lib/feature-libraries/chunked-forest/codec/compressedEncode.js +12 -1
  193. package/lib/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
  194. package/lib/feature-libraries/chunked-forest/codec/index.d.ts +1 -1
  195. package/lib/feature-libraries/chunked-forest/codec/index.d.ts.map +1 -1
  196. package/lib/feature-libraries/chunked-forest/codec/index.js +1 -1
  197. package/lib/feature-libraries/chunked-forest/codec/index.js.map +1 -1
  198. package/lib/feature-libraries/chunked-forest/codec/nodeEncoder.d.ts +1 -1
  199. package/lib/feature-libraries/chunked-forest/codec/nodeEncoder.d.ts.map +1 -1
  200. package/lib/feature-libraries/chunked-forest/codec/nodeEncoder.js +3 -15
  201. package/lib/feature-libraries/chunked-forest/codec/nodeEncoder.js.map +1 -1
  202. package/lib/feature-libraries/chunked-forest/index.d.ts +1 -1
  203. package/lib/feature-libraries/chunked-forest/index.d.ts.map +1 -1
  204. package/lib/feature-libraries/chunked-forest/index.js +1 -1
  205. package/lib/feature-libraries/chunked-forest/index.js.map +1 -1
  206. package/lib/feature-libraries/forest-summary/codec.d.ts +3 -3
  207. package/lib/feature-libraries/forest-summary/codec.d.ts.map +1 -1
  208. package/lib/feature-libraries/forest-summary/codec.js.map +1 -1
  209. package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts +3 -2
  210. package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
  211. package/lib/feature-libraries/forest-summary/forestSummarizer.js +4 -6
  212. package/lib/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
  213. package/lib/feature-libraries/index.d.ts +1 -1
  214. package/lib/feature-libraries/index.d.ts.map +1 -1
  215. package/lib/feature-libraries/index.js +1 -1
  216. package/lib/feature-libraries/index.js.map +1 -1
  217. package/lib/feature-libraries/modular-schema/modularChangeCodecV1.d.ts.map +1 -1
  218. package/lib/feature-libraries/modular-schema/modularChangeCodecV1.js +10 -10
  219. package/lib/feature-libraries/modular-schema/modularChangeCodecV1.js.map +1 -1
  220. package/lib/index.d.ts +2 -2
  221. package/lib/index.d.ts.map +1 -1
  222. package/lib/index.js +1 -1
  223. package/lib/index.js.map +1 -1
  224. package/lib/packageVersion.d.ts +1 -1
  225. package/lib/packageVersion.js +1 -1
  226. package/lib/packageVersion.js.map +1 -1
  227. package/lib/shared-tree/independentView.d.ts.map +1 -1
  228. package/lib/shared-tree/independentView.js +5 -8
  229. package/lib/shared-tree/independentView.js.map +1 -1
  230. package/lib/shared-tree/index.d.ts +1 -1
  231. package/lib/shared-tree/index.d.ts.map +1 -1
  232. package/lib/shared-tree/index.js.map +1 -1
  233. package/lib/shared-tree/schematizingTreeView.d.ts +5 -5
  234. package/lib/shared-tree/schematizingTreeView.d.ts.map +1 -1
  235. package/lib/shared-tree/schematizingTreeView.js.map +1 -1
  236. package/lib/shared-tree/sharedTree.d.ts +5 -0
  237. package/lib/shared-tree/sharedTree.d.ts.map +1 -1
  238. package/lib/shared-tree/sharedTree.js +8 -5
  239. package/lib/shared-tree/sharedTree.js.map +1 -1
  240. package/lib/shared-tree/transactionPostProcessor.d.ts +28 -0
  241. package/lib/shared-tree/transactionPostProcessor.d.ts.map +1 -0
  242. package/lib/shared-tree/transactionPostProcessor.js +23 -0
  243. package/lib/shared-tree/transactionPostProcessor.js.map +1 -0
  244. package/lib/shared-tree/treeAlpha.js +3 -3
  245. package/lib/shared-tree/treeAlpha.js.map +1 -1
  246. package/lib/shared-tree/treeCheckout.d.ts +16 -12
  247. package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
  248. package/lib/shared-tree/treeCheckout.js +18 -9
  249. package/lib/shared-tree/treeCheckout.js.map +1 -1
  250. package/lib/shared-tree/unhydratedTreeContext.d.ts +5 -5
  251. package/lib/shared-tree/unhydratedTreeContext.d.ts.map +1 -1
  252. package/lib/shared-tree/unhydratedTreeContext.js.map +1 -1
  253. package/lib/shared-tree-core/editManagerCodecs.d.ts +3 -2
  254. package/lib/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
  255. package/lib/shared-tree-core/editManagerCodecs.js.map +1 -1
  256. package/lib/shared-tree-core/editManagerCodecsCommons.d.ts +3 -9
  257. package/lib/shared-tree-core/editManagerCodecsCommons.d.ts.map +1 -1
  258. package/lib/shared-tree-core/editManagerCodecsCommons.js +3 -5
  259. package/lib/shared-tree-core/editManagerCodecsCommons.js.map +1 -1
  260. package/lib/shared-tree-core/editManagerCodecsV1toV4.d.ts +3 -3
  261. package/lib/shared-tree-core/editManagerCodecsV1toV4.d.ts.map +1 -1
  262. package/lib/shared-tree-core/editManagerCodecsV1toV4.js.map +1 -1
  263. package/lib/shared-tree-core/editManagerCodecsVSharedBranches.d.ts +3 -3
  264. package/lib/shared-tree-core/editManagerCodecsVSharedBranches.d.ts.map +1 -1
  265. package/lib/shared-tree-core/editManagerCodecsVSharedBranches.js.map +1 -1
  266. package/lib/shared-tree-core/editManagerSummarizer.d.ts +5 -9
  267. package/lib/shared-tree-core/editManagerSummarizer.d.ts.map +1 -1
  268. package/lib/shared-tree-core/editManagerSummarizer.js +5 -10
  269. package/lib/shared-tree-core/editManagerSummarizer.js.map +1 -1
  270. package/lib/shared-tree-core/index.d.ts +1 -1
  271. package/lib/shared-tree-core/index.d.ts.map +1 -1
  272. package/lib/shared-tree-core/index.js +1 -1
  273. package/lib/shared-tree-core/index.js.map +1 -1
  274. package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  275. package/lib/shared-tree-core/sharedTreeCore.js +3 -1
  276. package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
  277. package/lib/shared-tree-core/transaction.d.ts +84 -9
  278. package/lib/shared-tree-core/transaction.d.ts.map +1 -1
  279. package/lib/shared-tree-core/transaction.js +73 -6
  280. package/lib/shared-tree-core/transaction.js.map +1 -1
  281. package/lib/simple-tree/api/index.d.ts +1 -1
  282. package/lib/simple-tree/api/index.d.ts.map +1 -1
  283. package/lib/simple-tree/api/index.js.map +1 -1
  284. package/lib/simple-tree/api/schemaCreationUtilities.d.ts +3 -3
  285. package/lib/simple-tree/api/schemaCreationUtilities.js +3 -3
  286. package/lib/simple-tree/api/schemaCreationUtilities.js.map +1 -1
  287. package/lib/simple-tree/api/schemaFactory.d.ts +1 -1
  288. package/lib/simple-tree/api/schemaFactory.js +2 -2
  289. package/lib/simple-tree/api/schemaFactory.js.map +1 -1
  290. package/lib/simple-tree/api/snapshotCompatibilityChecker.d.ts +2 -2
  291. package/lib/simple-tree/api/snapshotCompatibilityChecker.js +2 -2
  292. package/lib/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
  293. package/lib/simple-tree/api/transactionTypes.d.ts +91 -28
  294. package/lib/simple-tree/api/transactionTypes.d.ts.map +1 -1
  295. package/lib/simple-tree/api/transactionTypes.js.map +1 -1
  296. package/lib/simple-tree/api/tree.d.ts +86 -22
  297. package/lib/simple-tree/api/tree.d.ts.map +1 -1
  298. package/lib/simple-tree/api/tree.js.map +1 -1
  299. package/lib/simple-tree/api/treeBeta.d.ts.map +1 -1
  300. package/lib/simple-tree/api/treeBeta.js.map +1 -1
  301. package/lib/simple-tree/core/treeNodeSchema.d.ts +7 -7
  302. package/lib/simple-tree/core/treeNodeSchema.js.map +1 -1
  303. package/lib/simple-tree/index.d.ts +1 -1
  304. package/lib/simple-tree/index.d.ts.map +1 -1
  305. package/lib/simple-tree/index.js.map +1 -1
  306. package/lib/text/index.d.ts +1 -0
  307. package/lib/text/index.d.ts.map +1 -1
  308. package/lib/text/index.js +1 -0
  309. package/lib/text/index.js.map +1 -1
  310. package/lib/text/textDomainFormatted.d.ts +69 -758
  311. package/lib/text/textDomainFormatted.d.ts.map +1 -1
  312. package/lib/text/textDomainFormatted.js +277 -324
  313. package/lib/text/textDomainFormatted.js.map +1 -1
  314. package/lib/text/textDomainFormattedDefault.d.ts +273 -0
  315. package/lib/text/textDomainFormattedDefault.d.ts.map +1 -0
  316. package/lib/text/textDomainFormattedDefault.js +91 -0
  317. package/lib/text/textDomainFormattedDefault.js.map +1 -0
  318. package/lib/util/compressedIds.d.ts +118 -0
  319. package/lib/util/compressedIds.d.ts.map +1 -0
  320. package/lib/util/compressedIds.js +124 -0
  321. package/lib/util/compressedIds.js.map +1 -0
  322. package/lib/util/index.d.ts +1 -0
  323. package/lib/util/index.d.ts.map +1 -1
  324. package/lib/util/index.js +1 -0
  325. package/lib/util/index.js.map +1 -1
  326. package/package.json +23 -23
  327. package/src/core/change-family/changeFamily.ts +3 -12
  328. package/src/core/rebase/types.ts +2 -2
  329. package/src/core/schema-stored/storedSchemaRepository.ts +2 -2
  330. package/src/entrypoints/alpha.ts +12 -8
  331. package/src/entrypoints/beta.ts +8 -0
  332. package/src/entrypoints/legacy.ts +8 -0
  333. package/src/feature-libraries/chunked-forest/codec/chunkDecoding.ts +35 -64
  334. package/src/feature-libraries/chunked-forest/codec/codecs.ts +138 -42
  335. package/src/feature-libraries/chunked-forest/codec/compressedEncode.ts +32 -4
  336. package/src/feature-libraries/chunked-forest/codec/index.ts +1 -0
  337. package/src/feature-libraries/chunked-forest/codec/nodeEncoder.ts +7 -19
  338. package/src/feature-libraries/chunked-forest/index.ts +3 -0
  339. package/src/feature-libraries/forest-summary/codec.ts +5 -3
  340. package/src/feature-libraries/forest-summary/forestSummarizer.ts +4 -5
  341. package/src/feature-libraries/index.ts +3 -0
  342. package/src/feature-libraries/modular-schema/modularChangeCodecV1.ts +13 -9
  343. package/src/index.ts +13 -8
  344. package/src/packageVersion.ts +1 -1
  345. package/src/shared-tree/independentView.ts +5 -10
  346. package/src/shared-tree/index.ts +1 -0
  347. package/src/shared-tree/schematizingTreeView.ts +25 -25
  348. package/src/shared-tree/sharedTree.ts +16 -4
  349. package/src/shared-tree/transactionPostProcessor.ts +40 -0
  350. package/src/shared-tree/treeAlpha.ts +3 -3
  351. package/src/shared-tree/treeCheckout.ts +61 -44
  352. package/src/shared-tree/unhydratedTreeContext.ts +18 -15
  353. package/src/shared-tree-core/editManagerCodecs.ts +3 -2
  354. package/src/shared-tree-core/editManagerCodecsCommons.ts +10 -14
  355. package/src/shared-tree-core/editManagerCodecsV1toV4.ts +3 -2
  356. package/src/shared-tree-core/editManagerCodecsVSharedBranches.ts +3 -2
  357. package/src/shared-tree-core/editManagerSummarizer.ts +5 -9
  358. package/src/shared-tree-core/index.ts +8 -5
  359. package/src/shared-tree-core/sharedTreeCore.ts +3 -2
  360. package/src/shared-tree-core/transaction.ts +148 -18
  361. package/src/simple-tree/api/index.ts +13 -9
  362. package/src/simple-tree/api/schemaCreationUtilities.ts +3 -3
  363. package/src/simple-tree/api/schemaFactory.ts +2 -2
  364. package/src/simple-tree/api/snapshotCompatibilityChecker.ts +2 -2
  365. package/src/simple-tree/api/transactionTypes.ts +111 -39
  366. package/src/simple-tree/api/tree.ts +141 -47
  367. package/src/simple-tree/api/treeBeta.ts +2 -0
  368. package/src/simple-tree/core/treeNodeSchema.ts +7 -7
  369. package/src/simple-tree/index.ts +12 -8
  370. package/src/text/index.ts +1 -0
  371. package/src/text/textDomainFormatted.ts +520 -415
  372. package/src/text/textDomainFormattedDefault.ts +150 -0
  373. package/src/util/compressedIds.ts +216 -0
  374. package/src/util/index.ts +15 -0
@@ -11,23 +11,35 @@ import {
11
11
  forEachNodeSubsequence,
12
12
  type FieldKey,
13
13
  type ITreeCursorSynchronous,
14
+ type TreeValue,
14
15
  } from "../core/index.js";
15
16
  import { currentObserver, buildNodeComparator } from "../feature-libraries/index.js";
16
- import { TreeAlpha } from "../shared-tree/index.js";
17
+ import { TreeAlpha, Tree as TreeStatic } from "../shared-tree/index.js";
17
18
  import {
18
- enumFromStrings,
19
- eraseSchemaDetails,
20
19
  getInnerNode,
21
20
  SchemaFactory,
22
21
  SchemaFactoryAlpha,
23
22
  TreeArrayNode,
24
23
  TreeBeta,
24
+ createCustomizedFluidFrameworkScopedFactory,
25
+ isObjectNodeSchema,
26
+ eraseSchemaDetailsSubclassable,
25
27
  } from "../simple-tree/index.js";
26
28
  import type {
29
+ TreeNodeSchema,
30
+ LazyItem,
31
+ ImplicitAllowedTypes,
32
+ TreeFieldFromImplicitField,
27
33
  InsertableTypedNode,
28
- TreeNode,
29
34
  TreeNodeFromImplicitAllowedTypes,
30
- WithType,
35
+ InsertableTreeNodeFromImplicitAllowedTypes,
36
+ InsertableTreeFieldFromImplicitField,
37
+ NodeKind,
38
+ TreeNode,
39
+ ScopedSchemaName,
40
+ ErasedSchemaSubclassable,
41
+ ErasedNode,
42
+ SchemaFactoryBeta,
31
43
  } from "../simple-tree/index.js";
32
44
  import { brand, mapIterable, validateIndex, validateIndexRange } from "../util/index.js";
33
45
 
@@ -37,464 +49,488 @@ import {
37
49
  type TextAsTree,
38
50
  } from "./textDomain.js";
39
51
 
40
- const sf = new SchemaFactoryAlpha("com.fluidframework.text.formatted");
41
-
42
- class TextNode
43
- extends sf.object("Text", {
44
- content: SchemaFactory.required([() => StringArray], { key: EmptyKey }),
45
- })
46
- implements FormattedTextAsTree.Members
47
- {
48
- public defaultFormat: FormattedTextAsTree.CharacterFormat =
49
- new FormattedTextAsTree.CharacterFormat(defaultFormat);
50
-
51
- public insertAt(index: number, additionalCharacters: string): void {
52
- this.content.insertAt(
53
- index,
54
- TreeArrayNode.spread(textAtomsFromString(additionalCharacters, this.defaultFormat)),
55
- );
56
- }
52
+ /**
53
+ * Sets up scope for formatted text schema built-in types.
54
+ * @remarks User-provided factory scoping will be applied as `com.fluidframework.text.formatted<user-scope>`.
55
+ */
56
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- Inferring is the most practical option here
57
+ function createFormattedScopedFactory<TUserScope extends string>(
58
+ inputSchemaFactory: SchemaFactoryBeta<TUserScope>,
59
+ ) {
60
+ return createCustomizedFluidFrameworkScopedFactory(inputSchemaFactory, "text.formatted");
61
+ }
57
62
 
58
- public removeRange(index: number | undefined, end: number | undefined): void {
59
- this.content.removeRange(index, end);
60
- }
63
+ /**
64
+ * Schema factory for formatted text types which are not generic.
65
+ */
66
+ const sfStatic = new SchemaFactoryAlpha("com.fluidframework.text.formatted");
61
67
 
62
- public characters(): Iterable<string> {
63
- return mapIterable(this.content, (atom) => atom.content.content);
64
- }
68
+ const formatKey: FieldKey = brand("format");
65
69
 
66
- public charactersCopy(): string[] {
67
- const result = this.content.charactersCopy();
68
- debugAssert(
69
- () =>
70
- compareArrays(result, this.charactersCopy_reference()) ||
71
- "invalid charactersCopy optimizations",
72
- );
73
- return result;
74
- }
70
+ /**
71
+ * A collection of text related types, schema and utilities for working with text beyond the basic {@link SchemaStatics.string}.
72
+ * @remarks
73
+ * This is generic over formatting an embedded object/atom types.
74
+ * See {@link FormattedTextAsTreeDefault} for a default parameterization.
75
+ * @privateRemarks
76
+ * TODO:
77
+ * - Add more comprehensive tests for generic parameterizations other than default.
78
+ * - Sort out API around overwriting subsets of formatting information.
79
+ * @internal
80
+ */
81
+ export namespace FormattedTextAsTree {
82
+ /**
83
+ * Factory for formatted text schema as a function of the formatting and the embedded object (atom) types.
84
+ */
85
+ export function createSchema<
86
+ const TUserScope extends string,
87
+ const FormatSchema extends ImplicitAllowedTypes,
88
+ const ExtraAtomsSchema extends readonly LazyItem<
89
+ TreeNodeSchema<string, NodeKind, TextAtom & TreeNode>
90
+ >[],
91
+ >(
92
+ inputSchemaFactory: SchemaFactoryBeta<TUserScope>,
93
+ formatSchema: FormatSchema,
94
+ extraAtoms: ExtraAtomsSchema,
95
+ defaultFormatInsertable: InsertableTreeFieldFromImplicitField<FormatSchema>,
96
+ ): FormattedTextSchema<TUserScope, FormatSchema, ExtraAtomsSchema> {
97
+ const atoms = [StringTextAtom, ...extraAtoms] as const;
98
+
99
+ const sf = createFormattedScopedFactory(inputSchemaFactory);
100
+ class TextNode
101
+ extends sf.object("Text", {
102
+ content: SchemaFactory.required([() => StringArray], { key: EmptyKey }),
103
+ })
104
+ implements FormattedTextMembers<FormatSchema, ExtraAtomsSchema>
105
+ {
106
+ public defaultFormat: TreeFieldFromImplicitField<FormatSchema> =
107
+ TreeBeta.create<FormatSchema>(formatSchema, defaultFormatInsertable);
108
+
109
+ public insertAt(index: number, additionalCharacters: string): void {
110
+ this.content.insertAt(
111
+ index,
112
+ TreeArrayNode.spread(textAtomsFromString(additionalCharacters, this.defaultFormat)),
113
+ );
114
+ }
75
115
 
76
- public characterCount(): number {
77
- return this.content.length;
78
- }
116
+ public removeRange(index: number | undefined, end: number | undefined): void {
117
+ this.content.removeRange(index, end);
118
+ }
79
119
 
80
- public fullString(): string {
81
- const result = this.content.fullString();
82
- debugAssert(
83
- () => result === this.fullString_reference() || "invalid fullString optimizations",
84
- );
85
- return result;
86
- }
120
+ public characters(): Iterable<string> {
121
+ return mapIterable(this.content, (atom) => atom.content.content);
122
+ }
87
123
 
88
- /**
89
- * A non-optimized reference implementation of fullString.
90
- */
91
- public fullString_reference(): string {
92
- return [...this.characters()].join("");
93
- }
124
+ public charactersCopy(): string[] {
125
+ const result = this.content.charactersCopy();
126
+ debugAssert(
127
+ () =>
128
+ compareArrays(result, this.charactersCopy_reference()) ||
129
+ "invalid charactersCopy optimizations",
130
+ );
131
+ return result;
132
+ }
94
133
 
95
- /**
96
- * Unoptimized trivially correct implementation of charactersCopy.
97
- */
98
- public charactersCopy_reference(): string[] {
99
- return [...this.characters()];
100
- }
134
+ public characterCount(): number {
135
+ return this.content.length;
136
+ }
101
137
 
102
- public static fromString(
103
- value: string,
104
- format?: FormattedTextAsTree.CharacterFormat,
105
- ): TextNode {
106
- // Constructing an ArrayNode from an iterator is supported, so creating an array from the iterable of characters seems like it's not necessary here,
107
- // but to reduce the risk of incorrect data interpretation, we actually ban this in the special case where the iterable is a string directly, which is the case here.
108
- // Thus the array construction here is necessary to avoid a runtime error.
109
- return new TextNode({
110
- content: [
111
- ...textAtomsFromString(
112
- value,
113
- format ?? new FormattedTextAsTree.CharacterFormat(defaultFormat),
114
- ),
115
- ],
116
- });
117
- }
138
+ public fullString(): string {
139
+ const result = this.content.fullString();
140
+ debugAssert(
141
+ () => result === this.fullString_reference() || "invalid fullString optimizations",
142
+ );
143
+ return result;
144
+ }
118
145
 
119
- public charactersWithFormatting(): readonly FormattedTextAsTree.StringAtom[] {
120
- return this.content;
121
- }
122
- public insertWithFormattingAt(
123
- index: number,
124
- additionalCharacters: Iterable<InsertableTypedNode<typeof FormattedTextAsTree.StringAtom>>,
125
- ): void {
126
- this.content.insertAt(index, TreeArrayNode.spread(additionalCharacters));
127
- }
128
- public formatRange(
129
- start: number | undefined,
130
- end: number | undefined,
131
- format: Partial<FormattedTextAsTree.CharacterFormat>,
132
- ): void {
133
- const formatStart = start ?? 0;
134
- validateIndex(formatStart, this.content, "FormattedTextAsTree.formatRange", true);
135
-
136
- const formatEnd = Math.min(this.content.length, end ?? this.content.length);
137
- validateIndexRange(
138
- formatStart,
139
- formatEnd,
140
- this.content,
141
- "FormattedTextAsTree.formatRange",
142
- );
143
-
144
- const branch = TreeAlpha.branch(this);
145
-
146
- const applyFormatting = (): void => {
147
- for (let i = formatStart; i < formatEnd; i++) {
148
- const atom = this.content[i];
149
- if (atom === undefined) {
150
- throw new UsageError("Index out of bounds while formatting text range.");
151
- }
152
- for (const [key, value] of Object.entries(format) as [
153
- keyof FormattedTextAsTree.CharacterFormat,
154
- unknown,
155
- ][]) {
156
- // Object.entries should only return string keyed enumerable own properties.
157
- // The TypeScript typing does not account for this, and thus this assertion is necessary for this code to compile.
158
- assert(
159
- typeof key === "string",
160
- 0xcc8 /* Object.entries returned a non-string key. */,
161
- );
162
- const f = FormattedTextAsTree.CharacterFormat.fields.get(key);
163
- if (f === undefined) {
164
- throw new UsageError(`Unknown format key: ${key}`);
165
- }
166
- // Ensures that if the input is a node, it is cloned before being inserted into the tree.
167
- atom.format[key] = TreeBeta.clone(TreeBeta.create(f, value as never)) as never;
168
- }
146
+ /**
147
+ * A non-optimized reference implementation of fullString.
148
+ */
149
+ public fullString_reference(): string {
150
+ return [...this.characters()].join("");
169
151
  }
170
- };
171
-
172
- if (branch === undefined) {
173
- // If this node does not have a corresponding branch, then it is unhydrated.
174
- // I.e., it is not part of a collaborative session yet.
175
- // Therefore, we don't need to run the edits as a transaction.
176
- // Note: for unhydrated nodes each atom edit fires a separate `treeChanged` event,
177
- // so formatting N atoms will produce N callbacks on `onContentChanged` subscribers
178
- // instead of the single callback that hydrated (transacted) edits produce.
179
- // `withBufferedTreeEvents` is not a viable mitigation here: when more than one atom's
180
- // `format` field changes within the same buffered scope, the kernel's per-field
181
- // dedup logic discards the delta (see `treeNodeKernel.ts` `#fieldMarksBuffer`),
182
- // which is worse for incremental consumers than N well-formed callbacks.
183
- // Use `runTransaction` on a hydrated node (i.e. after inserting into the document)
184
- // if batched events matter.
185
- applyFormatting();
186
- } else {
187
- // Wrap all formatting operations in a single transaction for atomicity.
188
- branch.runTransaction(() => {
189
- applyFormatting();
190
- });
191
- }
192
- }
193
- /**
194
- * Returns the character string at the given atom index, or `undefined` if out of bounds.
195
- * @remarks
196
- * Line atoms expand to `"\n"`; text atoms return their underlying code point(s).
197
- */
198
- private getAtomCharacterAt(index: number): string | undefined {
199
- const atom = this.content[index];
200
- if (atom === undefined) return undefined;
201
- return atom.content instanceof FormattedTextAsTree.StringLineAtom
202
- ? "\n"
203
- : atom.content.content;
204
- }
205
152
 
206
- public onCharactersChanged(
207
- callback: (ops: readonly TextAsTree.TextOp[] | undefined) => void,
208
- ): () => void {
209
- return TreeAlpha.on(this.content, "nodeChanged", ({ delta }) =>
210
- processCharactersChangedDelta(
211
- delta,
212
- (index) => this.getAtomCharacterAt(index),
213
- callback,
214
- ),
215
- );
216
- }
153
+ /**
154
+ * Unoptimized trivially correct implementation of charactersCopy.
155
+ */
156
+ public charactersCopy_reference(): string[] {
157
+ return [...this.characters()];
158
+ }
217
159
 
218
- public onContentChanged(
219
- callback: (ops: readonly TextAsTree.TextOp[] | undefined) => void,
220
- ): () => void {
221
- return TreeAlpha.on(this.content, "treeChanged", ({ delta }) =>
222
- processCharactersChangedDelta(
223
- delta,
224
- (index) => this.getAtomCharacterAt(index),
225
- callback,
226
- ),
227
- );
228
- }
160
+ public static fromString(
161
+ value: string,
162
+ format?: TreeFieldFromImplicitField<FormatSchema>,
163
+ ): TextNode {
164
+ // Use `this` rather than `TextNode` so the more derived schema class is constructed when using this as a static on a subclass.
165
+ return new this({
166
+ content: [
167
+ // Constructing an ArrayNode from an iterator is supported, so creating an array from the iterable of characters seems like it's not necessary here,
168
+ // but to reduce the risk of incorrect data interpretation, we actually ban this in the special case where the iterable is a string directly, which is the case here.
169
+ // Thus the array construction here is necessary to avoid a runtime error.
170
+ ...textAtomsFromString(
171
+ value,
172
+ format ?? TreeBeta.create<FormatSchema>(formatSchema, defaultFormatInsertable),
173
+ ),
174
+ ],
175
+ });
176
+ }
229
177
 
230
- public getUniformRun(startIndex: number, endIndex?: number): number {
231
- return this.content.getUniformRun(startIndex, endIndex);
232
- }
233
- public getString(startIndex: number, endIndex?: number): string {
234
- return this.content.getString(startIndex, endIndex);
235
- }
236
- }
178
+ public charactersWithFormatting(): readonly StringAtom[] {
179
+ return this.content;
180
+ }
181
+ public insertWithFormattingAt(
182
+ index: number,
183
+ additionalCharacters: Iterable<InsertableTypedNode<typeof StringAtom>>,
184
+ ): void {
185
+ this.content.insertAt(index, TreeArrayNode.spread(additionalCharacters));
186
+ }
237
187
 
238
- const defaultFormat = {
239
- bold: false,
240
- italic: false,
241
- underline: false,
242
- size: 12,
243
- font: "Arial",
244
- } as const;
188
+ public formatRange(
189
+ start: number | undefined,
190
+ end: number | undefined,
191
+ format: Partial<TreeNodeFromImplicitAllowedTypes<FormatSchema>>,
192
+ ): void {
193
+ const formatStart = start ?? 0;
194
+ validateIndex(formatStart, this.content, "FormattedTextAsTree.formatRange", true);
195
+
196
+ const formatEnd = Math.min(this.content.length, end ?? this.content.length);
197
+ validateIndexRange(
198
+ formatStart,
199
+ formatEnd,
200
+ this.content,
201
+ "FormattedTextAsTree.formatRange",
202
+ );
245
203
 
246
- const formatKey: FieldKey = brand("format");
204
+ const fieldFormats = Object.entries(format) as [
205
+ keyof TreeNodeFromImplicitAllowedTypes<FormatSchema>,
206
+ unknown,
207
+ ][];
208
+
209
+ TreeAlpha.context(this).runTransaction(() => {
210
+ for (let i = formatStart; i < formatEnd; i++) {
211
+ const atom = this.content[i];
212
+ // Range validated above, so this should never fail.
213
+ assert(
214
+ atom !== undefined,
215
+ 0xd08 /* Index out of bounds while formatting text range. */,
216
+ );
217
+ const formatNode: TreeNode | TreeValue = atom.format;
218
+ const atomFormatSchema = TreeStatic.schema(formatNode);
219
+ if (!isObjectNodeSchema(atomFormatSchema)) {
220
+ // TODO: redesign this API to work with all allowed FormatSchema types.
221
+ throw new UsageError(
222
+ "formatRange currently only supports object nodes for the format.",
223
+ );
224
+ }
225
+ for (const [key, value] of fieldFormats) {
226
+ // Object.entries should only return string keyed enumerable own properties.
227
+ // The TypeScript typing does not account for this, and thus this assertion is necessary for this code to compile.
228
+ assert(
229
+ typeof key === "string",
230
+ 0xcc8 /* Object.entries returned a non-string key. */,
231
+ );
232
+
233
+ const field = atomFormatSchema.fields.get(key);
234
+ if (field === undefined) {
235
+ throw new UsageError(`Unknown format key: ${key}`);
236
+ }
237
+
238
+ // Ensures that if the input is a node, it is cloned before being inserted into the tree.
239
+ const clonedValue = TreeBeta.clone(TreeBeta.create(field, value as never)) as
240
+ | TreeNode
241
+ | TreeValue;
242
+
243
+ (
244
+ formatNode as unknown as Record<
245
+ keyof TreeNodeFromImplicitAllowedTypes<FormatSchema>,
246
+ TreeNode | TreeValue
247
+ >
248
+ )[key] = clonedValue;
249
+ }
250
+ }
251
+ });
252
+ }
247
253
 
248
- function textAtomsFromString(
249
- value: string,
250
- format: FormattedTextAsTree.CharacterFormat,
251
- ): Iterable<FormattedTextAsTree.StringAtom> {
252
- const result = mapIterable(
253
- charactersFromString(value),
254
- (char) =>
255
- new FormattedTextAsTree.StringAtom({
256
- content: { content: char },
257
- format: TreeBeta.clone<typeof FormattedTextAsTree.CharacterFormat>(format),
258
- }),
259
- );
260
- return result;
261
- }
254
+ /**
255
+ * Returns the {@link FormattedTextAsTree.TextAtom.content} at the given atom index, or `undefined` if out of bounds.
256
+ */
257
+ private getAtomCharacterAt(index: number): string | undefined {
258
+ const atom = this.content[index];
259
+ if (atom === undefined) return undefined;
260
+ return atom.content.content;
261
+ }
262
262
 
263
- class StringArray extends sf.array("StringArray", [() => FormattedTextAsTree.StringAtom]) {
264
- public withBorrowedSequenceCursor<T>(f: (cursor: ITreeCursorSynchronous) => T): T {
265
- const innerNode = getInnerNode(this);
266
- // Since the cursor will be used to read content from the tree and won't track observations,
267
- // treat it as if it observed the whole subtree.
268
- currentObserver?.observeNodeDeep(innerNode);
269
- const cursor = innerNode.borrowCursor();
270
- cursor.enterField(EmptyKey);
271
- const result = f(cursor);
272
- cursor.exitField();
273
- return result;
274
- }
263
+ public onCharactersChanged(
264
+ callback: (ops: readonly TextAsTree.TextOp[] | undefined) => void,
265
+ ): () => void {
266
+ return TreeAlpha.on(this.content, "nodeChanged", ({ delta }) =>
267
+ processCharactersChangedDelta(
268
+ delta,
269
+ (index) => this.getAtomCharacterAt(index),
270
+ callback,
271
+ ),
272
+ );
273
+ }
275
274
 
276
- private getCharactersSubarray(startIndex: number, endIndex: number): string[] {
277
- return this.withBorrowedSequenceCursor((cursor) => {
278
- const result: string[] = [];
279
- forEachNodeSubsequence(cursor, startIndex, endIndex, () => {
280
- debugAssert(
281
- () =>
282
- cursor.type === FormattedTextAsTree.StringAtom.identifier ||
283
- "invalid fullString type optimizations",
275
+ public onContentChanged(
276
+ callback: (ops: readonly TextAsTree.TextOp[] | undefined) => void,
277
+ ): () => void {
278
+ return TreeAlpha.on(this.content, "treeChanged", ({ delta }) =>
279
+ processCharactersChangedDelta(
280
+ delta,
281
+ (index) => this.getAtomCharacterAt(index),
282
+ callback,
283
+ ),
284
284
  );
285
+ }
286
+
287
+ public getUniformRun(startIndex: number, endIndex?: number): number {
288
+ return this.content.getUniformRun(startIndex, endIndex);
289
+ }
290
+
291
+ public getString(startIndex: number, endIndex?: number): string {
292
+ return this.content.getString(startIndex, endIndex);
293
+ }
294
+ }
295
+
296
+ function textAtomsFromString(
297
+ value: string,
298
+ format: TreeFieldFromImplicitField<FormatSchema>,
299
+ ): Iterable<StringAtom> {
300
+ const result = mapIterable(charactersFromString(value), (char) => {
301
+ const textAtom = new StringTextAtom({ content: char });
302
+ const data = {
303
+ content: textAtom,
304
+ format: TreeBeta.clone<FormatSchema>(format),
305
+ };
306
+ return new StringAtom(data as never); // Generic break type safety here. TODO: try and make safer.
307
+ });
308
+ return result;
309
+ }
310
+
311
+ class StringArray extends sf.array("StringArray", [() => StringAtom]) {
312
+ public withBorrowedSequenceCursor<T>(f: (cursor: ITreeCursorSynchronous) => T): T {
313
+ const innerNode = getInnerNode(this);
314
+ // Since the cursor will be used to read content from the tree and won't track observations,
315
+ // treat it as if it observed the whole subtree.
316
+ currentObserver?.observeNodeDeep(innerNode);
317
+ const cursor = innerNode.borrowCursor();
285
318
  cursor.enterField(EmptyKey);
286
- cursor.enterNode(0);
287
- let content: string;
288
- switch (cursor.type) {
289
- case FormattedTextAsTree.StringTextAtom.identifier: {
290
- cursor.enterField(EmptyKey);
291
- cursor.enterNode(0);
292
- content = cursor.value as string;
319
+ const result = f(cursor);
320
+ cursor.exitField();
321
+ return result;
322
+ }
323
+
324
+ private getCharactersSubarray(startIndex: number, endIndex: number): string[] {
325
+ const slowPathIndexes: number[] = [];
326
+ const result: string[] = [];
327
+ this.withBorrowedSequenceCursor((cursor) => {
328
+ forEachNodeSubsequence(cursor, startIndex, endIndex, () => {
293
329
  debugAssert(
294
- () => typeof content === "string" || "invalid fullString type optimizations",
330
+ () =>
331
+ (cursor.type as string) === StringAtom.identifier ||
332
+ "invalid fullString type optimizations",
295
333
  );
334
+ cursor.enterField(EmptyKey);
335
+ cursor.enterNode(0);
336
+ let content: string;
337
+ switch (cursor.type) {
338
+ case StringTextAtom.identifier: {
339
+ cursor.enterField(EmptyKey);
340
+ cursor.enterNode(0);
341
+ content = cursor.value as string;
342
+ debugAssert(
343
+ () => typeof content === "string" || "invalid fullString type optimizations",
344
+ );
345
+ cursor.exitNode();
346
+ cursor.exitField();
347
+ break;
348
+ }
349
+ // TODO: we could optimize this for constant cases via an optional symbol on the atom schema holding the constant.
350
+ // A less general optimization could just include cases for build in types with constant values
351
+ // (like below commented code: currently this would cause a cyclical dependency but could be refactored).
352
+ // case FormattedTextAsTree.StringLineAtom.identifier: {
353
+ // content = "\n";
354
+ // break;
355
+ // }
356
+ default: {
357
+ slowPathIndexes.push(result.length);
358
+ content = ""; // Placeholder for slow path content
359
+ }
360
+ }
296
361
  cursor.exitNode();
297
362
  cursor.exitField();
298
- break;
299
- }
300
- case FormattedTextAsTree.StringLineAtom.identifier: {
301
- content = "\n";
302
- break;
303
- }
304
- default: {
305
- fail(0xcde /* Unsupported node type in text array */, () => `${cursor.type}`);
306
- }
363
+ result.push(content);
364
+ });
365
+ });
366
+
367
+ // Fill in slow path cases not optimized above.
368
+ for (const index of slowPathIndexes) {
369
+ const node =
370
+ this[index + startIndex] ??
371
+ fail(
372
+ 0xd09 /* getCharactersSubarray failed to find index after index range was checked */,
373
+ );
374
+ result[index] = node.content.content;
307
375
  }
308
- cursor.exitNode();
309
- cursor.exitField();
310
- result.push(content);
311
- });
312
- return result;
313
- });
314
- }
315
376
 
316
- public charactersCopy(): string[] {
317
- return this.getCharactersSubarray(0, this.length);
318
- }
377
+ return result;
378
+ }
319
379
 
320
- public fullString(): string {
321
- return this.charactersCopy().join("");
322
- }
380
+ public charactersCopy(): string[] {
381
+ return this.getCharactersSubarray(0, this.length);
382
+ }
323
383
 
324
- public getString(startIndex: number, endIndex: number = this.length): string {
325
- validateIndexRange(startIndex, endIndex, this, "FormattedTextAsTree.getString");
326
- return this.getCharactersSubarray(startIndex, endIndex).join("");
327
- }
384
+ public fullString(): string {
385
+ return this.charactersCopy().join("");
386
+ }
328
387
 
329
- public getUniformRun(startIndex: number, endIndex: number = this.length): number {
330
- validateIndexRange(startIndex, endIndex, this, "FormattedTextAsTree.getUniformRun");
331
- if (endIndex === startIndex) {
332
- throw new UsageError("endIndex must be greater than startIndex for getUniformRun.");
333
- }
334
- const arrayLength = this.length;
335
- return this.withBorrowedSequenceCursor((cursor) => {
336
- cursor.enterNode(startIndex);
337
-
338
- // Capture the content type of the first atom
339
- cursor.enterField(EmptyKey);
340
- cursor.enterNode(0);
341
- const contentType = cursor.type;
342
- cursor.exitNode();
343
- cursor.exitField();
344
-
345
- // Build a comparator from the format subtree of the first atom
346
- // This compares by field key
347
- cursor.enterField(formatKey);
348
- cursor.enterNode(0);
349
- const formatComparator = buildNodeComparator(cursor);
350
- cursor.exitNode();
351
- cursor.exitField();
352
-
353
- let runLength = 1;
354
- const limit = Math.min(endIndex, arrayLength) - startIndex;
355
-
356
- while (runLength < limit && cursor.nextNode()) {
357
- // Compare atom type
358
- cursor.enterField(EmptyKey);
359
- cursor.enterNode(0);
360
- const typeMatches = cursor.type === contentType;
361
- cursor.exitNode();
362
- cursor.exitField();
363
- if (!typeMatches) {
364
- break;
388
+ public getString(startIndex: number, endIndex: number = this.length): string {
389
+ validateIndexRange(startIndex, endIndex, this, "FormattedTextAsTree.getString");
390
+ return this.getCharactersSubarray(startIndex, endIndex).join("");
391
+ }
392
+
393
+ public getUniformRun(startIndex: number, endIndex: number = this.length): number {
394
+ validateIndexRange(startIndex, endIndex, this, "FormattedTextAsTree.getUniformRun");
395
+ if (endIndex === startIndex) {
396
+ throw new UsageError("endIndex must be greater than startIndex for getUniformRun.");
365
397
  }
398
+ const arrayLength = this.length;
399
+ return this.withBorrowedSequenceCursor((cursor) => {
400
+ cursor.enterNode(startIndex);
401
+
402
+ // Capture the content type of the first atom
403
+ cursor.enterField(EmptyKey);
404
+ cursor.enterNode(0);
405
+ const contentType = cursor.type;
406
+ cursor.exitNode();
407
+ cursor.exitField();
408
+
409
+ // Build a comparator from the format subtree of the first atom
410
+ // This compares by field key
411
+ cursor.enterField(formatKey);
412
+ cursor.enterNode(0);
413
+ const formatComparator = buildNodeComparator(cursor);
414
+ cursor.exitNode();
415
+ cursor.exitField();
416
+
417
+ let runLength = 1;
418
+ const limit = Math.min(endIndex, arrayLength) - startIndex;
419
+
420
+ while (runLength < limit && cursor.nextNode()) {
421
+ // Compare atom type
422
+ cursor.enterField(EmptyKey);
423
+ cursor.enterNode(0);
424
+ const typeMatches = cursor.type === contentType;
425
+ cursor.exitNode();
426
+ cursor.exitField();
427
+ if (!typeMatches) {
428
+ break;
429
+ }
366
430
 
367
- // Compare format subtree using the compiled comparator
368
- cursor.enterField(formatKey);
369
- cursor.enterNode(0);
370
- const formatMatches = formatComparator(cursor);
371
- cursor.exitNode();
372
- cursor.exitField();
431
+ // Compare format subtree using the compiled comparator
432
+ cursor.enterField(formatKey);
433
+ cursor.enterNode(0);
434
+ const formatMatches = formatComparator(cursor);
435
+ cursor.exitNode();
436
+ cursor.exitField();
373
437
 
374
- if (formatMatches !== true) {
375
- break;
376
- }
438
+ if (formatMatches !== true) {
439
+ break;
440
+ }
377
441
 
378
- runLength++;
442
+ runLength++;
443
+ }
444
+ cursor.exitNode();
445
+ return runLength;
446
+ });
379
447
  }
380
- cursor.exitNode();
381
- return runLength;
382
- });
383
- }
384
- }
448
+ }
385
449
 
386
- /**
387
- * A collection of text related types, schema and utilities for working with text beyond the basic {@link SchemaStatics.string}.
388
- * @privateRemarks
389
- * This has hard-coded assumptions about what kind of embedded content and what kind of formatting is supported.
390
- * We will want to generalize this with a more generic schema factory function like with table.
391
- * Then either that and/or the output from it can be package exported.
392
- * This version is just an initial prototype.
393
- * @internal
394
- */
395
- export namespace FormattedTextAsTree {
396
- /**
397
- * Formatting options for characters.
398
- * @internal
399
- */
400
- export class CharacterFormat extends sf.objectAlpha("CharacterFormat", {
401
- bold: SchemaFactory.boolean,
402
- italic: SchemaFactory.boolean,
403
- underline: SchemaFactory.boolean,
404
- size: SchemaFactory.number,
405
- font: SchemaFactory.string,
406
- }) {}
450
+ /**
451
+ * A unit of the text, with formatting.
452
+ */
453
+ class StringAtom
454
+ extends sf.object("StringAtom", {
455
+ content: SchemaFactory.required(atoms, { key: EmptyKey }),
456
+ format: SchemaFactory.required(formatSchema),
457
+ })
458
+ implements
459
+ FormattedAtom<
460
+ TreeNodeFromImplicitAllowedTypes<FormatSchema>,
461
+ TreeNodeFromImplicitAllowedTypes<typeof atoms>
462
+ > {}
407
463
 
408
- /**
409
- * Unit in the string representing a single character.
410
- * @internal
411
- */
412
- export class StringTextAtom extends sf.object("StringTextAtom", {
413
464
  /**
414
- * The underlying text content of this atom.
465
+ * Schema for a text node.
415
466
  * @remarks
416
- * This is typically a single Unicode code point, and thus may contain multiple UTF-16 surrogate pair code units.
417
- * Using longer strings is still valid. For example, so users might store whole grapheme clusters here, or even longer sections of text.
418
- * Anything combined into a single atom will be treated atomically, and can not be partially selected or formatted.
419
- * Using larger atoms and splitting them as needed is NOT a recommended approach, since this will result in poor merge behavior for concurrent edits.
420
- * Instead atoms should always be the smallest unit of text which will be independently selected, moved or formatted.
421
- * @privateRemarks
422
- * This content logically represents the whole atom's content, so using {@link EmptyKey} makes sense to help indicate that.
467
+ * See {@link FormattedTextAsTree.Members} for the API.
468
+ * See {@link FormattedTextAsTree.Statics} for static APIs on this Schema, including construction.
423
469
  */
424
- content: SchemaFactory.required([SchemaFactory.string], { key: EmptyKey }),
425
- }) {}
426
-
427
- /**
428
- * Tag with which a line in text can be formatted from HTML.
429
- * @internal
430
- */
431
- export const LineTag = enumFromStrings(sf.scopedFactory("lineTag"), [
432
- "h1",
433
- "h2",
434
- "h3",
435
- "h4",
436
- "h5",
437
- "li",
438
- "ol",
439
- "checked",
440
- "unchecked",
441
- "blockquote",
442
- "codeBlock",
443
- ]);
444
- /**
445
- * {@inheritdoc FormattedTextAsTree.(LineTag:variable)}
446
- * @internal
447
- */
448
- export type LineTag = TreeNodeFromImplicitAllowedTypes<typeof LineTag.schema>;
470
+ const Tree = eraseSchemaDetailsSubclassable<
471
+ FormattedTextMembers<FormatSchema, ExtraAtomsSchema>,
472
+ Statics<Tree>
473
+ >()(TextNode);
474
+ type Tree = ErasedNode<
475
+ FormattedTextMembers<FormatSchema, ExtraAtomsSchema>,
476
+ FormattedTextSchemaIdentifier<TUserScope>
477
+ >;
478
+
479
+ return Tree;
480
+ }
449
481
 
450
482
  /**
451
- * Unit in the string representing a new line character with line formatting.
452
- * @remarks
453
- * This aligns with how Quill represents line formatting.
454
- * Quill formats line attributes (headers, list, blockquote, etc... ) on the newline character
455
- * and only lines using this atom can have line-specific formatting.
456
- * The optional indent level mirrors Quill's indent attribute,
457
- * which is applies to the line before the line break.
458
- * Any tagged line can be indented independently.
483
+ * Portion of a string with formatting.
484
+ * @sealed
459
485
  * @internal
460
486
  */
461
- export class StringLineAtom extends sf.object("StringLineAtom", {
462
- tag: LineTag.schema,
463
- indent: SchemaFactory.number,
464
- }) {
465
- public readonly content = "\n";
487
+ export interface FormattedAtom<TFormat, TText> {
488
+ readonly content: TText;
489
+ format: TFormat;
466
490
  }
467
491
 
468
492
  /**
469
- * Types of "atoms" that make up the text.
470
- * @internal
471
- */
472
- export const StringAtomContent = [StringTextAtom, StringLineAtom] as const;
473
- /**
474
- * {@inheritdoc FormattedTextAsTree.(StringAtomContent:variable)}
493
+ * Portion of a string.
475
494
  * @internal
476
495
  */
477
- export type StringAtomContent = TreeNodeFromImplicitAllowedTypes<typeof StringAtomContent>;
496
+ export interface TextAtom {
497
+ /**
498
+ * The content of the text atom, viewed as a string.
499
+ */
500
+ readonly content: string;
501
+ }
478
502
 
479
503
  /**
480
- * A unit of the text, with formatting.
504
+ * Unit in the string representing a single character.
481
505
  * @internal
482
506
  */
483
- export class StringAtom extends sf.object("StringAtom", {
484
- content: SchemaFactory.required(StringAtomContent, { key: EmptyKey }),
485
- format: CharacterFormat,
486
- }) {}
507
+ export class StringTextAtom
508
+ extends sfStatic.object("StringTextAtom", {
509
+ /**
510
+ * The underlying text content of this atom.
511
+ * @remarks
512
+ * This is typically a single Unicode code point, and thus may contain multiple UTF-16 surrogate pair code units.
513
+ * Using longer strings is still valid. For example, so users might store whole grapheme clusters here, or even longer sections of text.
514
+ * Anything combined into a single atom will be treated atomically, and can not be partially selected or formatted.
515
+ * Using larger atoms and splitting them as needed is NOT a recommended approach, since this will result in poor merge behavior for concurrent edits.
516
+ * Instead atoms should always be the smallest unit of text which will be independently selected, moved or formatted.
517
+ * @privateRemarks
518
+ * This content logically represents the whole atom's content, so using {@link EmptyKey} makes sense to help indicate that.
519
+ */
520
+ content: SchemaFactory.required([SchemaFactory.string], { key: EmptyKey }),
521
+ })
522
+ implements TextAtom {}
487
523
 
488
524
  /**
489
525
  * Statics for text nodes.
490
526
  * @internal
491
527
  */
492
- export interface Statics {
528
+ export interface Statics<TTree> {
493
529
  /**
494
- * Construct a {@link FormattedTextAsTree.(Tree:type)} from a string, where each character (as defined by iterating over the string) becomes a single character in the text node.
530
+ * Construct a node of `this` schema from a string, where each character (as defined by iterating over the string) becomes a single character in the text node.
495
531
  * @remarks This combines pairs of utf-16 surrogate code units into single characters as appropriate.
496
532
  */
497
- fromString(value: string): Tree;
533
+ fromString(value: string): TTree;
498
534
  }
499
535
 
500
536
  /**
@@ -511,10 +547,11 @@ export namespace FormattedTextAsTree {
511
547
  * and navigation/selection (which typically uses grapheme clusters).
512
548
  *
513
549
  * @see {@link FormattedTextAsTree.Statics.fromString} for construction.
514
- * @see {@link FormattedTextAsTree.(Tree:type)} for schema.
550
+ * @see {@link FormattedTextAsTree.createSchema} for creating schemas whose nodes implement this.
515
551
  * @internal
516
552
  */
517
- export interface Members extends TextAsTree.Members {
553
+ export interface Members<TFormatTree, TPartialFormat, TFormattedAtom, TFormattedInsert>
554
+ extends TextAsTree.Members {
518
555
  /**
519
556
  * Format to use by default for text inserted with non-formatted APIs.
520
557
  * @remarks
@@ -522,7 +559,7 @@ export namespace FormattedTextAsTree {
522
559
  * @privateRemarks
523
560
  * Opt this into observation tracking.
524
561
  */
525
- defaultFormat: CharacterFormat;
562
+ defaultFormat: TFormatTree;
526
563
 
527
564
  /**
528
565
  * Gets an array type view of the characters currently in the text.
@@ -533,7 +570,7 @@ export namespace FormattedTextAsTree {
533
570
  * We might not want to leak a node like this in the API.
534
571
  * Providing a way to index and iterate separately might be better.
535
572
  */
536
- charactersWithFormatting(): readonly StringAtom[];
573
+ charactersWithFormatting(): readonly TFormattedAtom[];
537
574
 
538
575
  /**
539
576
  * Insert a range of characters into the string based on character index.
@@ -550,7 +587,7 @@ export namespace FormattedTextAsTree {
550
587
  */
551
588
  insertWithFormattingAt(
552
589
  index: number,
553
- additionalCharacters: Iterable<InsertableTypedNode<typeof StringAtom>>,
590
+ additionalCharacters: Iterable<TFormattedInsert>,
554
591
  ): void;
555
592
 
556
593
  /**
@@ -564,7 +601,7 @@ export namespace FormattedTextAsTree {
564
601
  formatRange(
565
602
  startIndex: number | undefined,
566
603
  endIndex: number | undefined,
567
- format: Partial<CharacterFormat>,
604
+ format: TPartialFormat,
568
605
  ): void;
569
606
 
570
607
  /**
@@ -603,12 +640,80 @@ export namespace FormattedTextAsTree {
603
640
  }
604
641
 
605
642
  /**
606
- * Schema for a text node.
607
- * @remarks
608
- * See {@link FormattedTextAsTree.Members} for the API.
609
- * See {@link FormattedTextAsTree.Statics} for static APIs on this Schema, including construction.
643
+ * Insertable shape for a formatted text atom used by {@link FormattedTextAsTree.Members.insertWithFormattingAt}.
644
+ * @internal
645
+ */
646
+ export interface FormattedAtomInsertable<TFormat, TContent> {
647
+ readonly content: TContent;
648
+ readonly format: TFormat;
649
+ }
650
+
651
+ /**
652
+ * Schema identifier for the a generic formatted text schema.
653
+ * @privateRemarks
654
+ * Eventually this should probably be given a better name and/or made a system type in a system namespace.
655
+ * @internal
656
+ */
657
+ export type FormattedTextSchemaIdentifier<TUserScope extends string> = ScopedSchemaName<
658
+ `com.fluidframework.text.formatted<${TUserScope}>`,
659
+ "Text"
660
+ >;
661
+
662
+ /**
663
+ * Helper for expressing the full set of formatted text atoms for a given schema.
664
+ * @privateRemarks
665
+ * Eventually this should probably be given a better name and/or made a system type in a system namespace.
666
+ * @internal
667
+ */
668
+ export type FormattedTextAtoms<
669
+ ExtraAtomsSchema extends readonly LazyItem<
670
+ TreeNodeSchema<string, NodeKind, TextAtom & TreeNode>
671
+ >[],
672
+ > = readonly [typeof StringTextAtom, ...ExtraAtomsSchema];
673
+
674
+ /**
675
+ * Helper for configuring {@link FormattedTextAsTree.Members}.
676
+ * @privateRemarks
677
+ * Eventually this should probably be inlined into `FormattedTextAsTree.Members` or made a system type in a system namespace.
678
+ * The approach should be evaluated after settling on a redesign of the `formatRange` API as that will impact what the type parameters are.
679
+ * @internal
680
+ */
681
+ export type FormattedTextMembers<
682
+ FormatSchema extends ImplicitAllowedTypes,
683
+ ExtraAtomsSchema extends readonly LazyItem<
684
+ TreeNodeSchema<string, NodeKind, TextAtom & TreeNode>
685
+ >[],
686
+ > = Members<
687
+ TreeFieldFromImplicitField<FormatSchema>,
688
+ Partial<TreeNodeFromImplicitAllowedTypes<FormatSchema>>,
689
+ FormattedAtom<
690
+ TreeNodeFromImplicitAllowedTypes<FormatSchema>,
691
+ TreeNodeFromImplicitAllowedTypes<FormattedTextAtoms<ExtraAtomsSchema>>
692
+ >,
693
+ FormattedAtomInsertable<
694
+ InsertableTreeNodeFromImplicitAllowedTypes<FormatSchema>,
695
+ InsertableTreeNodeFromImplicitAllowedTypes<FormattedTextAtoms<ExtraAtomsSchema>>
696
+ >
697
+ >;
698
+
699
+ /**
700
+ * A generic type for a formatted text schema.
610
701
  * @internal
611
702
  */
612
- export const Tree = eraseSchemaDetails<Members, Statics>()(TextNode);
613
- export type Tree = Members & TreeNode & WithType<"com.fluidframework.text.formatted.Text">;
703
+ export type FormattedTextSchema<
704
+ TUserScope extends string,
705
+ FormatSchema extends ImplicitAllowedTypes,
706
+ ExtraAtomsSchema extends readonly LazyItem<
707
+ TreeNodeSchema<string, NodeKind, TextAtom & TreeNode>
708
+ >[],
709
+ > = Statics<
710
+ ErasedNode<
711
+ FormattedTextMembers<FormatSchema, ExtraAtomsSchema>,
712
+ FormattedTextSchemaIdentifier<TUserScope>
713
+ >
714
+ > &
715
+ ErasedSchemaSubclassable<
716
+ FormattedTextMembers<FormatSchema, ExtraAtomsSchema>,
717
+ FormattedTextSchemaIdentifier<TUserScope>
718
+ >;
614
719
  }