@fluidframework/tree 2.101.1 → 2.102.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.
- package/CHANGELOG.md +4 -0
- package/api-report/tree.alpha.api.md +19 -0
- package/dist/codec/codec.d.ts +22 -53
- package/dist/codec/codec.d.ts.map +1 -1
- package/dist/codec/codec.js +7 -44
- package/dist/codec/codec.js.map +1 -1
- package/dist/codec/index.d.ts +1 -1
- package/dist/codec/index.d.ts.map +1 -1
- package/dist/codec/index.js +2 -2
- package/dist/codec/index.js.map +1 -1
- package/dist/codec/versioned/codec.d.ts +56 -28
- package/dist/codec/versioned/codec.d.ts.map +1 -1
- package/dist/codec/versioned/codec.js +29 -12
- package/dist/codec/versioned/codec.js.map +1 -1
- package/dist/codec/versioned/index.d.ts +1 -1
- package/dist/codec/versioned/index.d.ts.map +1 -1
- package/dist/codec/versioned/index.js +2 -2
- package/dist/codec/versioned/index.js.map +1 -1
- package/dist/core/tree/deltaUtil.d.ts +2 -2
- package/dist/core/tree/deltaUtil.js +2 -2
- package/dist/core/tree/deltaUtil.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecV1.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecV1.js +3 -2
- package/dist/core/tree/detachedFieldIndexCodecV1.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecV2.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecV2.js +4 -2
- package/dist/core/tree/detachedFieldIndexCodecV2.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecs.d.ts +3 -3
- package/dist/core/tree/detachedFieldIndexCodecs.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecs.js +1 -1
- package/dist/core/tree/detachedFieldIndexCodecs.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkTree.d.ts +38 -1
- package/dist/feature-libraries/chunked-forest/chunkTree.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkTree.js +62 -4
- package/dist/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.js +18 -4
- package/dist/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts +34 -2
- package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.js +106 -3
- package/dist/feature-libraries/chunked-forest/codec/chunkDecoding.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts +5 -5
- package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/codecs.js +2 -2
- package/dist/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/compressedEncode.d.ts +7 -7
- package/dist/feature-libraries/chunked-forest/codec/compressedEncode.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/format/formatV2.d.ts +25 -5
- package/dist/feature-libraries/chunked-forest/codec/format/formatV2.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/format/formatV2.js +9 -3
- package/dist/feature-libraries/chunked-forest/codec/format/formatV2.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/format/formatVText.d.ts +105 -0
- package/dist/feature-libraries/chunked-forest/codec/format/formatVText.d.ts.map +1 -0
- package/dist/feature-libraries/chunked-forest/codec/format/formatVText.js +44 -0
- package/dist/feature-libraries/chunked-forest/codec/format/formatVText.js.map +1 -0
- package/dist/feature-libraries/chunked-forest/codec/format/index.d.ts +2 -1
- package/dist/feature-libraries/chunked-forest/codec/format/index.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/format/index.js +5 -1
- package/dist/feature-libraries/chunked-forest/codec/format/index.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/format/versions.d.ts +38 -4
- package/dist/feature-libraries/chunked-forest/codec/format/versions.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/format/versions.js +7 -1
- package/dist/feature-libraries/chunked-forest/codec/format/versions.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/nodeEncoder.d.ts +7 -7
- package/dist/feature-libraries/chunked-forest/codec/nodeEncoder.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/nodeEncoder.js +1 -2
- package/dist/feature-libraries/chunked-forest/codec/nodeEncoder.js.map +1 -1
- package/dist/feature-libraries/forest-summary/codec.d.ts +5 -4
- package/dist/feature-libraries/forest-summary/codec.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/codec.js +2 -2
- package/dist/feature-libraries/forest-summary/codec.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
- package/dist/feature-libraries/schema-index/codec.d.ts +2 -2
- package/dist/feature-libraries/schema-index/codec.d.ts.map +1 -1
- package/dist/feature-libraries/schema-index/codec.js +1 -1
- package/dist/feature-libraries/schema-index/codec.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/shared-tree/isAuditableFromOutcome.d.ts +20 -0
- package/dist/shared-tree/isAuditableFromOutcome.d.ts.map +1 -0
- package/dist/shared-tree/isAuditableFromOutcome.js +36 -0
- package/dist/shared-tree/isAuditableFromOutcome.js.map +1 -0
- package/dist/shared-tree/treeCheckout.d.ts +2 -0
- package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
- package/dist/shared-tree/treeCheckout.js +37 -8
- package/dist/shared-tree/treeCheckout.js.map +1 -1
- package/dist/shared-tree-core/editManagerCodecs.d.ts +3 -3
- package/dist/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerCodecs.js +2 -2
- package/dist/shared-tree-core/editManagerCodecs.js.map +1 -1
- package/dist/shared-tree-core/messageCodecs.d.ts +3 -3
- package/dist/shared-tree-core/messageCodecs.d.ts.map +1 -1
- package/dist/shared-tree-core/messageCodecs.js +2 -2
- package/dist/shared-tree-core/messageCodecs.js.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.js +25 -11
- package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/dist/text/codePointUtils.d.ts +48 -0
- package/dist/text/codePointUtils.d.ts.map +1 -0
- package/dist/text/codePointUtils.js +80 -0
- package/dist/text/codePointUtils.js.map +1 -0
- package/dist/text/index.d.ts +1 -0
- package/dist/text/index.d.ts.map +1 -1
- package/dist/text/index.js +4 -1
- package/dist/text/index.js.map +1 -1
- package/dist/text/textDomain.d.ts +93 -1
- package/dist/text/textDomain.d.ts.map +1 -1
- package/dist/text/textDomain.js +65 -8
- package/dist/text/textDomain.js.map +1 -1
- package/dist/text/textDomainFormatted.d.ts +24 -6
- package/dist/text/textDomainFormatted.d.ts.map +1 -1
- package/dist/text/textDomainFormatted.js +29 -1
- package/dist/text/textDomainFormatted.js.map +1 -1
- package/dist/treeFactory.d.ts.map +1 -1
- package/dist/treeFactory.js +9 -7
- package/dist/treeFactory.js.map +1 -1
- package/dist/util/breakable.d.ts +7 -1
- package/dist/util/breakable.d.ts.map +1 -1
- package/dist/util/breakable.js +18 -4
- package/dist/util/breakable.js.map +1 -1
- package/lib/codec/codec.d.ts +22 -53
- package/lib/codec/codec.d.ts.map +1 -1
- package/lib/codec/codec.js +7 -44
- package/lib/codec/codec.js.map +1 -1
- package/lib/codec/index.d.ts +1 -1
- package/lib/codec/index.d.ts.map +1 -1
- package/lib/codec/index.js +1 -1
- package/lib/codec/index.js.map +1 -1
- package/lib/codec/versioned/codec.d.ts +56 -28
- package/lib/codec/versioned/codec.d.ts.map +1 -1
- package/lib/codec/versioned/codec.js +27 -10
- package/lib/codec/versioned/codec.js.map +1 -1
- package/lib/codec/versioned/index.d.ts +1 -1
- package/lib/codec/versioned/index.d.ts.map +1 -1
- package/lib/codec/versioned/index.js +1 -1
- package/lib/codec/versioned/index.js.map +1 -1
- package/lib/core/tree/deltaUtil.d.ts +2 -2
- package/lib/core/tree/deltaUtil.js +2 -2
- package/lib/core/tree/deltaUtil.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecV1.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecV1.js +3 -2
- package/lib/core/tree/detachedFieldIndexCodecV1.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecV2.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecV2.js +5 -3
- package/lib/core/tree/detachedFieldIndexCodecV2.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecs.d.ts +3 -3
- package/lib/core/tree/detachedFieldIndexCodecs.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecs.js +2 -2
- package/lib/core/tree/detachedFieldIndexCodecs.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkTree.d.ts +38 -1
- package/lib/feature-libraries/chunked-forest/chunkTree.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkTree.js +61 -4
- package/lib/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.js +20 -6
- package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts +34 -2
- package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.js +102 -2
- package/lib/feature-libraries/chunked-forest/codec/chunkDecoding.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts +5 -5
- package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/codecs.js +3 -3
- package/lib/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/compressedEncode.d.ts +7 -7
- package/lib/feature-libraries/chunked-forest/codec/compressedEncode.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/compressedEncode.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/format/formatV2.d.ts +25 -5
- package/lib/feature-libraries/chunked-forest/codec/format/formatV2.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/format/formatV2.js +8 -2
- package/lib/feature-libraries/chunked-forest/codec/format/formatV2.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/format/formatVText.d.ts +105 -0
- package/lib/feature-libraries/chunked-forest/codec/format/formatVText.d.ts.map +1 -0
- package/lib/feature-libraries/chunked-forest/codec/format/formatVText.js +41 -0
- package/lib/feature-libraries/chunked-forest/codec/format/formatVText.js.map +1 -0
- package/lib/feature-libraries/chunked-forest/codec/format/index.d.ts +2 -1
- package/lib/feature-libraries/chunked-forest/codec/format/index.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/format/index.js +2 -1
- package/lib/feature-libraries/chunked-forest/codec/format/index.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/format/versions.d.ts +38 -4
- package/lib/feature-libraries/chunked-forest/codec/format/versions.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/format/versions.js +6 -0
- package/lib/feature-libraries/chunked-forest/codec/format/versions.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/nodeEncoder.d.ts +7 -7
- package/lib/feature-libraries/chunked-forest/codec/nodeEncoder.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/nodeEncoder.js +2 -3
- package/lib/feature-libraries/chunked-forest/codec/nodeEncoder.js.map +1 -1
- package/lib/feature-libraries/forest-summary/codec.d.ts +5 -4
- package/lib/feature-libraries/forest-summary/codec.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/codec.js +3 -3
- package/lib/feature-libraries/forest-summary/codec.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeTypes.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeTypes.js.map +1 -1
- package/lib/feature-libraries/schema-index/codec.d.ts +2 -2
- package/lib/feature-libraries/schema-index/codec.d.ts.map +1 -1
- package/lib/feature-libraries/schema-index/codec.js +2 -2
- package/lib/feature-libraries/schema-index/codec.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/shared-tree/isAuditableFromOutcome.d.ts +20 -0
- package/lib/shared-tree/isAuditableFromOutcome.d.ts.map +1 -0
- package/lib/shared-tree/isAuditableFromOutcome.js +32 -0
- package/lib/shared-tree/isAuditableFromOutcome.js.map +1 -0
- package/lib/shared-tree/treeCheckout.d.ts +2 -0
- package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
- package/lib/shared-tree/treeCheckout.js +37 -8
- package/lib/shared-tree/treeCheckout.js.map +1 -1
- package/lib/shared-tree-core/editManagerCodecs.d.ts +3 -3
- package/lib/shared-tree-core/editManagerCodecs.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerCodecs.js +3 -3
- package/lib/shared-tree-core/editManagerCodecs.js.map +1 -1
- package/lib/shared-tree-core/messageCodecs.d.ts +3 -3
- package/lib/shared-tree-core/messageCodecs.d.ts.map +1 -1
- package/lib/shared-tree-core/messageCodecs.js +3 -3
- package/lib/shared-tree-core/messageCodecs.js.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.js +25 -11
- package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/lib/text/codePointUtils.d.ts +48 -0
- package/lib/text/codePointUtils.d.ts.map +1 -0
- package/lib/text/codePointUtils.js +75 -0
- package/lib/text/codePointUtils.js.map +1 -0
- package/lib/text/index.d.ts +1 -0
- package/lib/text/index.d.ts.map +1 -1
- package/lib/text/index.js +1 -0
- package/lib/text/index.js.map +1 -1
- package/lib/text/textDomain.d.ts +93 -1
- package/lib/text/textDomain.d.ts.map +1 -1
- package/lib/text/textDomain.js +56 -0
- package/lib/text/textDomain.js.map +1 -1
- package/lib/text/textDomainFormatted.d.ts +24 -6
- package/lib/text/textDomainFormatted.d.ts.map +1 -1
- package/lib/text/textDomainFormatted.js +30 -2
- package/lib/text/textDomainFormatted.js.map +1 -1
- package/lib/treeFactory.d.ts.map +1 -1
- package/lib/treeFactory.js +2 -0
- package/lib/treeFactory.js.map +1 -1
- package/lib/util/breakable.d.ts +7 -1
- package/lib/util/breakable.d.ts.map +1 -1
- package/lib/util/breakable.js +18 -4
- package/lib/util/breakable.js.map +1 -1
- package/package.json +24 -24
- package/src/codec/codec.ts +82 -73
- package/src/codec/index.ts +2 -1
- package/src/codec/versioned/codec.ts +173 -73
- package/src/codec/versioned/index.ts +2 -1
- package/src/core/tree/deltaUtil.ts +2 -2
- package/src/core/tree/detachedFieldIndexCodecV1.ts +3 -2
- package/src/core/tree/detachedFieldIndexCodecV2.ts +5 -3
- package/src/core/tree/detachedFieldIndexCodecs.ts +2 -2
- package/src/feature-libraries/chunked-forest/chunkTree.ts +85 -1
- package/src/feature-libraries/chunked-forest/chunkedForest.ts +27 -7
- package/src/feature-libraries/chunked-forest/codec/chunkDecoding.ts +143 -7
- package/src/feature-libraries/chunked-forest/codec/codecs.ts +30 -28
- package/src/feature-libraries/chunked-forest/codec/compressedEncode.ts +7 -7
- package/src/feature-libraries/chunked-forest/codec/format/formatV2.ts +11 -7
- package/src/feature-libraries/chunked-forest/codec/format/formatVText.ts +83 -0
- package/src/feature-libraries/chunked-forest/codec/format/index.ts +6 -1
- package/src/feature-libraries/chunked-forest/codec/format/versions.ts +25 -4
- package/src/feature-libraries/chunked-forest/codec/nodeEncoder.ts +14 -18
- package/src/feature-libraries/forest-summary/codec.ts +9 -4
- package/src/feature-libraries/modular-schema/modularChangeTypes.ts +1 -1
- package/src/feature-libraries/schema-index/codec.ts +2 -5
- package/src/index.ts +6 -1
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/isAuditableFromOutcome.ts +34 -0
- package/src/shared-tree/treeCheckout.ts +52 -9
- package/src/shared-tree-core/editManagerCodecs.ts +4 -6
- package/src/shared-tree-core/messageCodecs.ts +4 -4
- package/src/simple-tree/core/treeNodeKernel.ts +27 -13
- package/src/text/codePointUtils.ts +81 -0
- package/src/text/index.ts +1 -0
- package/src/text/textDomain.ts +155 -2
- package/src/text/textDomainFormatted.ts +73 -2
- package/src/treeFactory.ts +5 -0
- package/src/util/breakable.ts +27 -6
package/src/text/textDomain.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { typeFactory as tf } from "@fluidframework/type-factory/internal";
|
|
15
15
|
|
|
16
16
|
import { EmptyKey, mapCursorField, type ITreeCursorSynchronous } from "../core/index.js";
|
|
17
|
+
import { TreeAlpha } from "../shared-tree/index.js";
|
|
17
18
|
import {
|
|
18
19
|
eraseSchemaDetails,
|
|
19
20
|
getInnerNode,
|
|
@@ -21,8 +22,13 @@ import {
|
|
|
21
22
|
SchemaFactoryAlpha,
|
|
22
23
|
TreeArrayNode,
|
|
23
24
|
} from "../simple-tree/index.js";
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
import type {
|
|
26
|
+
ArrayNodeDeltaOp,
|
|
27
|
+
ArrayNodeTreeChangedDeltaOp,
|
|
28
|
+
TreeNode,
|
|
29
|
+
WithType,
|
|
30
|
+
// eslint-disable-next-line import-x/no-duplicates
|
|
31
|
+
} from "../simple-tree/index.js";
|
|
26
32
|
// Add some unused imports which show up in the generated d.ts file.
|
|
27
33
|
// This prevents them from getting inline imports generated, cleaning up the d.ts file and API reports.
|
|
28
34
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports, import-x/no-duplicates
|
|
@@ -144,6 +150,14 @@ class TextNode
|
|
|
144
150
|
return [...this.content];
|
|
145
151
|
}
|
|
146
152
|
|
|
153
|
+
public onCharactersChanged(
|
|
154
|
+
callback: (ops: readonly TextAsTree.TextOp[] | undefined) => void,
|
|
155
|
+
): () => void {
|
|
156
|
+
return TreeAlpha.on(this.content, "nodeChanged", ({ delta }) =>
|
|
157
|
+
processCharactersChangedDelta(delta, (i) => this.content[i], callback),
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
147
161
|
public static fromString(value: string): TextNode {
|
|
148
162
|
// Constructing an ArrayNode from an iterator is supported, so creating an array from the iterable of characters seems like its not necessary here,
|
|
149
163
|
// 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.
|
|
@@ -186,6 +200,63 @@ class StringArray extends sf.array("StringArray", SchemaFactory.string) {
|
|
|
186
200
|
}
|
|
187
201
|
}
|
|
188
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Processes an array-node delta into a {@link TextAsTree.TextOp}[] and calls `callback`.
|
|
205
|
+
* @remarks
|
|
206
|
+
* Shared by both the plain `onCharactersChanged` (from `nodeChanged`) and formatted `onContentChanged`
|
|
207
|
+
* (from `treeChanged`) implementations.
|
|
208
|
+
* @param delta - The raw array-node delta, or `undefined` when no delta is available.
|
|
209
|
+
* When retain ops carry `subtreeChanged` (i.e. delta comes from a `treeChanged` event), the emitted
|
|
210
|
+
* retain ops include an explicit `formattingChanged: boolean`. Otherwise `formattingChanged` is omitted.
|
|
211
|
+
* @param getCharacter - Returns the character string at the given array index in the **post-edit** tree.
|
|
212
|
+
* Only invoked for insert ops, where it must read the inserted character at the given index of the tree
|
|
213
|
+
* after the edit has been applied. Passing an accessor that reads pre-edit content will silently produce wrong text.
|
|
214
|
+
* Return `undefined` if the tree is out of sync with the delta; this triggers a full-reread fallback.
|
|
215
|
+
* @param callback - The user-supplied callback to invoke with the translated ops.
|
|
216
|
+
*/
|
|
217
|
+
export function processCharactersChangedDelta(
|
|
218
|
+
delta: readonly (ArrayNodeDeltaOp | ArrayNodeTreeChangedDeltaOp)[] | undefined,
|
|
219
|
+
getCharacter: (index: number) => string | undefined,
|
|
220
|
+
callback: (ops: readonly TextAsTree.TextOp[] | undefined) => void,
|
|
221
|
+
): void {
|
|
222
|
+
if (delta === undefined) {
|
|
223
|
+
callback(undefined);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
let readPosition = 0;
|
|
227
|
+
const ops: TextAsTree.TextOp[] = [];
|
|
228
|
+
for (const op of delta) {
|
|
229
|
+
if (op.type === "retain") {
|
|
230
|
+
// `subtreeChanged` is only present on retain ops from `treeChanged` deltas.
|
|
231
|
+
ops.push(
|
|
232
|
+
"subtreeChanged" in op
|
|
233
|
+
? { type: "retain", count: op.count, formattingChanged: op.subtreeChanged === true }
|
|
234
|
+
: { type: "retain", count: op.count },
|
|
235
|
+
);
|
|
236
|
+
readPosition += op.count;
|
|
237
|
+
} else if (op.type === "insert") {
|
|
238
|
+
// Accumulate into an array and join at the end to keep this O(n) for large inserts
|
|
239
|
+
// (paste of long text) instead of O(n^2) from repeated string concatenation.
|
|
240
|
+
const characters: string[] = [];
|
|
241
|
+
for (let i = 0; i < op.count; i++) {
|
|
242
|
+
const character = getCharacter(readPosition);
|
|
243
|
+
if (character === undefined) {
|
|
244
|
+
// Tree is out of sync with the delta — fall back to full re-read.
|
|
245
|
+
callback(undefined);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
characters.push(character);
|
|
249
|
+
readPosition++;
|
|
250
|
+
}
|
|
251
|
+
ops.push({ type: "insert", text: characters.join("") });
|
|
252
|
+
} else {
|
|
253
|
+
// Construct explicit remove op so internal fields on the source op don't leak.
|
|
254
|
+
ops.push({ type: "remove", count: op.count });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
callback(ops);
|
|
258
|
+
}
|
|
259
|
+
|
|
189
260
|
/**
|
|
190
261
|
* A collection of text related types, schema and utilities for working with text beyond the basic {@link SchemaStatics.string}.
|
|
191
262
|
* @privateRemarks
|
|
@@ -249,6 +320,71 @@ class StringArray extends sf.array("StringArray", SchemaFactory.string) {
|
|
|
249
320
|
* @alpha
|
|
250
321
|
*/
|
|
251
322
|
export namespace TextAsTree {
|
|
323
|
+
/**
|
|
324
|
+
* A retain op in a character-level delta — a span of unchanged characters that the consumer should skip over.
|
|
325
|
+
* @sealed
|
|
326
|
+
* @alpha
|
|
327
|
+
*/
|
|
328
|
+
export interface TextRetainOp {
|
|
329
|
+
/**
|
|
330
|
+
* Discriminator identifying this op as a retain.
|
|
331
|
+
*/
|
|
332
|
+
readonly type: "retain";
|
|
333
|
+
/**
|
|
334
|
+
* The number of Unicode code points to retain.
|
|
335
|
+
*/
|
|
336
|
+
readonly count: number;
|
|
337
|
+
/**
|
|
338
|
+
* Whether at least one character in the retained range had a deep change.
|
|
339
|
+
* @remarks
|
|
340
|
+
* Present only on retain ops delivered by {@link @fluidframework/tree#FormattedTextAsTree.Members.onContentChanged};
|
|
341
|
+
* always absent on retain ops delivered by {@link TextAsTree.Members.onCharactersChanged}.
|
|
342
|
+
* When present, `true` indicates the retained range contained a formatting property update
|
|
343
|
+
* or an atom content edit; `false` indicates no deep change.
|
|
344
|
+
*/
|
|
345
|
+
readonly formattingChanged?: boolean;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* An insert op in a character-level delta — characters newly added to the text.
|
|
350
|
+
* @remarks
|
|
351
|
+
* Carries the inserted text as a single string, which is more convenient for consumers than individual characters.
|
|
352
|
+
* @sealed
|
|
353
|
+
* @alpha
|
|
354
|
+
*/
|
|
355
|
+
export interface TextInsertOp {
|
|
356
|
+
/**
|
|
357
|
+
* Discriminator identifying this op as an insert.
|
|
358
|
+
*/
|
|
359
|
+
readonly type: "insert";
|
|
360
|
+
/**
|
|
361
|
+
* The newly inserted characters, concatenated into a single string.
|
|
362
|
+
*/
|
|
363
|
+
readonly text: string;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* A remove op in a character-level delta — a span of characters that has been deleted from the text.
|
|
368
|
+
* @sealed
|
|
369
|
+
* @alpha
|
|
370
|
+
*/
|
|
371
|
+
export interface TextRemoveOp {
|
|
372
|
+
/**
|
|
373
|
+
* Discriminator identifying this op as a remove.
|
|
374
|
+
*/
|
|
375
|
+
readonly type: "remove";
|
|
376
|
+
/**
|
|
377
|
+
* The number of Unicode code points removed.
|
|
378
|
+
*/
|
|
379
|
+
readonly count: number;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* A single operation in a character-level delta describing an insert, remove, or retain of text.
|
|
384
|
+
* @alpha
|
|
385
|
+
*/
|
|
386
|
+
export type TextOp = TextRetainOp | TextInsertOp | TextRemoveOp;
|
|
387
|
+
|
|
252
388
|
/**
|
|
253
389
|
* Statics for text nodes.
|
|
254
390
|
* @alpha
|
|
@@ -276,6 +412,7 @@ export namespace TextAsTree {
|
|
|
276
412
|
*
|
|
277
413
|
* @see {@link TextAsTree.Statics.fromString} for construction.
|
|
278
414
|
* @see {@link TextAsTree.(Tree:type)} for schema.
|
|
415
|
+
* @sealed
|
|
279
416
|
* @alpha
|
|
280
417
|
*/
|
|
281
418
|
export interface Members {
|
|
@@ -325,6 +462,22 @@ export namespace TextAsTree {
|
|
|
325
462
|
* See {@link (TreeArrayNode:interface).removeRange} for more details on the behavior.
|
|
326
463
|
*/
|
|
327
464
|
removeRange(startIndex: number | undefined, endIndex: number | undefined): void;
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Subscribe to shallow character-level changes on this text node — inserts and removes only.
|
|
468
|
+
* @param callback - Called after each change with a sequence of {@link TextAsTree.TextOp}s describing what changed,
|
|
469
|
+
* or `undefined` when a delta could not be computed (e.g. during a schema upgrade).
|
|
470
|
+
* @returns A cleanup function that unsubscribes the callback when called.
|
|
471
|
+
* @remarks
|
|
472
|
+
* Only fires on shallow changes — inserts and removes.
|
|
473
|
+
* It does not fire on deep changes such as formatting property updates on existing characters.
|
|
474
|
+
* For formatted text, use {@link @fluidframework/tree#FormattedTextAsTree.Members.onContentChanged} to also receive deep changes.
|
|
475
|
+
*
|
|
476
|
+
* All counts in the delivered ops are in Unicode code points, not UTF-16 code units.
|
|
477
|
+
* For characters outside the Basic Multilingual Plane (e.g. emoji), one code point
|
|
478
|
+
* corresponds to two UTF-16 code units — convert before using the counts as string indices.
|
|
479
|
+
*/
|
|
480
|
+
onCharactersChanged(callback: (ops: readonly TextOp[] | undefined) => void): () => void;
|
|
328
481
|
}
|
|
329
482
|
|
|
330
483
|
/**
|
|
@@ -31,7 +31,11 @@ import type {
|
|
|
31
31
|
} from "../simple-tree/index.js";
|
|
32
32
|
import { brand, mapIterable, validateIndex, validateIndexRange } from "../util/index.js";
|
|
33
33
|
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
charactersFromString,
|
|
36
|
+
processCharactersChangedDelta,
|
|
37
|
+
type TextAsTree,
|
|
38
|
+
} from "./textDomain.js";
|
|
35
39
|
|
|
36
40
|
const sf = new SchemaFactoryAlpha("com.fluidframework.text.formatted");
|
|
37
41
|
|
|
@@ -169,6 +173,15 @@ class TextNode
|
|
|
169
173
|
// If this node does not have a corresponding branch, then it is unhydrated.
|
|
170
174
|
// I.e., it is not part of a collaborative session yet.
|
|
171
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.
|
|
172
185
|
applyFormatting();
|
|
173
186
|
} else {
|
|
174
187
|
// Wrap all formatting operations in a single transaction for atomicity.
|
|
@@ -177,6 +190,43 @@ class TextNode
|
|
|
177
190
|
});
|
|
178
191
|
}
|
|
179
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
|
+
|
|
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
|
+
}
|
|
217
|
+
|
|
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
|
+
}
|
|
229
|
+
|
|
180
230
|
public getUniformRun(startIndex: number, endIndex?: number): number {
|
|
181
231
|
return this.content.getUniformRun(startIndex, endIndex);
|
|
182
232
|
}
|
|
@@ -363,7 +413,7 @@ export namespace FormattedTextAsTree {
|
|
|
363
413
|
/**
|
|
364
414
|
* The underlying text content of this atom.
|
|
365
415
|
* @remarks
|
|
366
|
-
* This is typically a single
|
|
416
|
+
* This is typically a single Unicode code point, and thus may contain multiple UTF-16 surrogate pair code units.
|
|
367
417
|
* Using longer strings is still valid. For example, so users might store whole grapheme clusters here, or even longer sections of text.
|
|
368
418
|
* Anything combined into a single atom will be treated atomically, and can not be partially selected or formatted.
|
|
369
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.
|
|
@@ -529,6 +579,27 @@ export namespace FormattedTextAsTree {
|
|
|
529
579
|
* @param endIndex - Optional ending index (exclusive). Defaults to the end of the text.
|
|
530
580
|
*/
|
|
531
581
|
getString(startIndex: number, endIndex?: number): string;
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Subscribe to all content changes on this text node, including both shallow
|
|
585
|
+
* changes (inserts/removes) and deep changes (formatting updates on existing characters).
|
|
586
|
+
* @param callback - Called after each change with a sequence of {@link TextAsTree.TextOp}s describing what changed,
|
|
587
|
+
* or `undefined` when a delta could not be computed (e.g. during a schema upgrade).
|
|
588
|
+
* @returns A cleanup function that unsubscribes the callback when called.
|
|
589
|
+
* @remarks
|
|
590
|
+
* Unlike {@link TextAsTree.Members.onCharactersChanged} which only fires on
|
|
591
|
+
* shallow changes (inserts and removes), this method also fires on deep changes —
|
|
592
|
+
* formatting property updates on existing characters.
|
|
593
|
+
* The {@link TextAsTree.TextRetainOp.formattingChanged} flag on retain ops
|
|
594
|
+
* indicates which character ranges had formatting updates.
|
|
595
|
+
*
|
|
596
|
+
* All counts in the delivered ops are in Unicode code points, not UTF-16 code units.
|
|
597
|
+
* For characters outside the Basic Multilingual Plane (e.g. emoji), one code point
|
|
598
|
+
* corresponds to two UTF-16 code units — convert before using the counts as string indices.
|
|
599
|
+
*/
|
|
600
|
+
onContentChanged(
|
|
601
|
+
callback: (ops: readonly TextAsTree.TextOp[] | undefined) => void,
|
|
602
|
+
): () => void;
|
|
532
603
|
}
|
|
533
604
|
|
|
534
605
|
/**
|
package/src/treeFactory.ts
CHANGED
|
@@ -17,6 +17,10 @@ import {
|
|
|
17
17
|
import { UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
18
18
|
|
|
19
19
|
import { FluidClientVersion, type FormatVersion } from "./codec/index.js";
|
|
20
|
+
import {
|
|
21
|
+
detachedFieldIndexCodecBuilder,
|
|
22
|
+
DetachedFieldIndexFormatVersion,
|
|
23
|
+
} from "./core/index.js";
|
|
20
24
|
import {
|
|
21
25
|
SharedTreeKernel,
|
|
22
26
|
type ITreePrivate,
|
|
@@ -227,6 +231,7 @@ const sharedBranchesOptions: SharedTreeOptionsInternal = {
|
|
|
227
231
|
writeVersionOverrides: new Map<string, FormatVersion>([
|
|
228
232
|
[editManagerCodecName, EditManagerFormatVersion.vSharedBranches],
|
|
229
233
|
[messageCodecName, MessageFormatVersion.vSharedBranches],
|
|
234
|
+
[detachedFieldIndexCodecBuilder.name, DetachedFieldIndexFormatVersion.v2],
|
|
230
235
|
]),
|
|
231
236
|
allowPossiblyIncompatibleWriteVersionOverrides: true,
|
|
232
237
|
};
|
package/src/util/breakable.ts
CHANGED
|
@@ -29,13 +29,14 @@ export class Breakable {
|
|
|
29
29
|
* @remarks
|
|
30
30
|
* Can use {@link throwIfBroken} to apply this to a method.
|
|
31
31
|
*/
|
|
32
|
-
public use(
|
|
32
|
+
public use(
|
|
33
|
+
message: (brokenBy: Error) => string = (brokenBy) =>
|
|
34
|
+
`Invalid use of ${this.name} after it was put into an invalid state by another error.\nOriginal Error:\n${brokenBy}`,
|
|
35
|
+
): void {
|
|
33
36
|
if (this.brokenBy !== undefined) {
|
|
34
|
-
const error = new UsageError(
|
|
35
|
-
`Invalid use of ${this.name} after it was put into an invalid state by another error.\nOriginal Error:\n${this.brokenBy}`,
|
|
36
|
-
);
|
|
37
|
+
const error = new UsageError(message(this.brokenBy));
|
|
37
38
|
|
|
38
|
-
// This "cause" field is added in ES2022, but using
|
|
39
|
+
// This "cause" field is added in ES2022, but using it even without that built in support, it is still helpful.
|
|
39
40
|
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
|
|
40
41
|
// TODO: remove this cast when targeting ES2022 lib or later.
|
|
41
42
|
(error as { cause?: unknown }).cause =
|
|
@@ -80,11 +81,31 @@ export class Breakable {
|
|
|
80
81
|
* Like {@link Breakable.use}, this also throws if already broken.
|
|
81
82
|
* Any exceptions this catches are re-thrown.
|
|
82
83
|
* Can use {@link breakingMethod} to apply this to a method.
|
|
84
|
+
*
|
|
85
|
+
* If `breaker` returns a Promise, the breakable is also broken if that Promise rejects, and the broken state is rechecked when it resolves:
|
|
86
|
+
* if the breakable was put into a broken state during the async operation (by some other code path), the resolved value is discarded and the returned Promise rejects with a {@link UsageError}.
|
|
87
|
+
*
|
|
88
|
+
* This does not serialize concurrent runs: a synchronous run invoked while an async run is in flight will execute immediately, and is only blocked if the breakable is already broken.
|
|
89
|
+
* Detection of an async result uses `instanceof Promise`, so custom Promise-like objects and Promises from other realms will be treated as synchronous results.
|
|
83
90
|
*/
|
|
84
91
|
public run<TResult>(breaker: () => TResult): TResult {
|
|
85
92
|
this.use();
|
|
86
93
|
try {
|
|
87
|
-
|
|
94
|
+
const result = breaker();
|
|
95
|
+
if (result instanceof Promise) {
|
|
96
|
+
return result.then(
|
|
97
|
+
(value: Awaited<TResult>) => {
|
|
98
|
+
// If broken while process was running: this will throw instead of returning the value.
|
|
99
|
+
this.use(
|
|
100
|
+
(brokenBy) =>
|
|
101
|
+
`${this.name} was put into a broken state during an async operation.\nOriginal Error:\n${brokenBy}`,
|
|
102
|
+
);
|
|
103
|
+
return value;
|
|
104
|
+
},
|
|
105
|
+
(error: unknown) => this.rethrowCaught(error),
|
|
106
|
+
) as TResult;
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
88
109
|
} catch (error: unknown) {
|
|
89
110
|
this.rethrowCaught(error);
|
|
90
111
|
}
|