@fluidframework/tree 2.10.0-305357 → 2.10.0-307060
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/.eslintrc.cjs +56 -25
- package/api-report/tree.alpha.api.md +14 -11
- package/api-report/tree.beta.api.md +5 -2
- package/api-report/tree.legacy.alpha.api.md +5 -2
- package/api-report/tree.legacy.public.api.md +5 -2
- package/api-report/tree.public.api.md +5 -2
- package/dist/core/forest/forest.d.ts +5 -1
- package/dist/core/forest/forest.d.ts.map +1 -1
- package/dist/core/forest/forest.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/schema-stored/storedSchemaRepository.d.ts +7 -3
- package/dist/core/schema-stored/storedSchemaRepository.d.ts.map +1 -1
- package/dist/core/schema-stored/storedSchemaRepository.js +4 -6
- package/dist/core/schema-stored/storedSchemaRepository.js.map +1 -1
- package/dist/core/tree/anchorSet.d.ts +8 -5
- package/dist/core/tree/anchorSet.d.ts.map +1 -1
- package/dist/core/tree/anchorSet.js +12 -11
- package/dist/core/tree/anchorSet.js.map +1 -1
- package/dist/events/emitter.d.ts +21 -9
- package/dist/events/emitter.d.ts.map +1 -1
- package/dist/events/emitter.js +36 -21
- package/dist/events/emitter.js.map +1 -1
- package/dist/events/listeners.d.ts +16 -5
- package/dist/events/listeners.d.ts.map +1 -1
- package/dist/events/listeners.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/basicChunk.d.ts +26 -5
- package/dist/feature-libraries/chunked-forest/basicChunk.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/basicChunk.js +15 -5
- package/dist/feature-libraries/chunked-forest/basicChunk.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkTree.js +1 -1
- package/dist/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts +3 -2
- package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.js +19 -9
- package/dist/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
- package/dist/feature-libraries/flex-tree/context.d.ts +3 -2
- package/dist/feature-libraries/flex-tree/context.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/context.js +3 -3
- package/dist/feature-libraries/flex-tree/context.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyField.js +1 -1
- package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyNode.js +1 -1
- package/dist/feature-libraries/flex-tree/lazyNode.js.map +1 -1
- package/dist/feature-libraries/flex-tree/utilities.js +1 -1
- package/dist/feature-libraries/flex-tree/utilities.js.map +1 -1
- package/dist/feature-libraries/index.d.ts +1 -1
- package/dist/feature-libraries/index.d.ts.map +1 -1
- package/dist/feature-libraries/index.js +2 -2
- package/dist/feature-libraries/index.js.map +1 -1
- package/dist/feature-libraries/modular-schema/comparison.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/comparison.js +3 -0
- package/dist/feature-libraries/modular-schema/comparison.js.map +1 -1
- package/dist/feature-libraries/modular-schema/discrepancies.d.ts +29 -29
- package/dist/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/discrepancies.js +121 -75
- package/dist/feature-libraries/modular-schema/discrepancies.js.map +1 -1
- package/dist/feature-libraries/modular-schema/genericFieldKind.js +2 -2
- package/dist/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
- package/dist/feature-libraries/modular-schema/index.d.ts +1 -1
- package/dist/feature-libraries/modular-schema/index.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/index.js +2 -2
- package/dist/feature-libraries/modular-schema/index.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFamily.js +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
- package/dist/feature-libraries/object-forest/objectForest.d.ts +2 -2
- package/dist/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
- package/dist/feature-libraries/object-forest/objectForest.js +6 -8
- package/dist/feature-libraries/object-forest/objectForest.js.map +1 -1
- package/dist/feature-libraries/schema-index/schemaSummarizer.js +1 -1
- package/dist/feature-libraries/schema-index/schemaSummarizer.js.map +1 -1
- package/dist/feature-libraries/sequence-field/compose.js +2 -2
- package/dist/feature-libraries/sequence-field/compose.js.map +1 -1
- package/dist/feature-libraries/sequence-field/markListFactory.js +1 -1
- package/dist/feature-libraries/sequence-field/markListFactory.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/schematizingTreeView.js +2 -2
- package/dist/shared-tree/schematizingTreeView.js.map +1 -1
- package/dist/shared-tree/treeApi.js +6 -3
- package/dist/shared-tree/treeApi.js.map +1 -1
- package/dist/shared-tree/treeCheckout.js +7 -7
- package/dist/shared-tree/treeCheckout.js.map +1 -1
- package/dist/shared-tree-core/branch.d.ts +7 -7
- package/dist/shared-tree-core/branch.d.ts.map +1 -1
- package/dist/shared-tree-core/branch.js +35 -25
- package/dist/shared-tree-core/branch.js.map +1 -1
- package/dist/shared-tree-core/editManager.js +4 -4
- package/dist/shared-tree-core/editManager.js.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.js +5 -5
- package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/dist/simple-tree/api/schemaCreationUtilities.d.ts +12 -14
- package/dist/simple-tree/api/schemaCreationUtilities.d.ts.map +1 -1
- package/dist/simple-tree/api/schemaCreationUtilities.js +9 -7
- package/dist/simple-tree/api/schemaCreationUtilities.js.map +1 -1
- package/dist/simple-tree/api/schemaFactory.d.ts +68 -10
- package/dist/simple-tree/api/schemaFactory.d.ts.map +1 -1
- package/dist/simple-tree/api/schemaFactory.js +38 -10
- package/dist/simple-tree/api/schemaFactory.js.map +1 -1
- package/dist/simple-tree/api/schemaFactoryRecursive.js.map +1 -1
- package/dist/simple-tree/api/treeNodeApi.js +4 -4
- package/dist/simple-tree/api/treeNodeApi.js.map +1 -1
- package/dist/simple-tree/arrayNode.js +1 -1
- package/dist/simple-tree/arrayNode.js.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.d.ts +7 -8
- package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.js +68 -73
- package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/dist/simple-tree/objectNode.d.ts +1 -1
- package/dist/simple-tree/objectNode.js.map +1 -1
- package/dist/simple-tree/objectNodeTypes.d.ts +3 -0
- package/dist/simple-tree/objectNodeTypes.d.ts.map +1 -1
- package/dist/simple-tree/objectNodeTypes.js +3 -1
- package/dist/simple-tree/objectNodeTypes.js.map +1 -1
- package/dist/simple-tree/proxies.js +1 -1
- package/dist/simple-tree/proxies.js.map +1 -1
- package/dist/simple-tree/schemaTypes.d.ts +26 -1
- package/dist/simple-tree/schemaTypes.d.ts.map +1 -1
- package/dist/simple-tree/schemaTypes.js.map +1 -1
- package/dist/simple-tree/treeNodeValid.js +2 -2
- package/dist/simple-tree/treeNodeValid.js.map +1 -1
- package/dist/util/nestedMap.d.ts.map +1 -1
- package/dist/util/nestedMap.js.map +1 -1
- package/docs/.attachments/object-merge-semantics.drawio +145 -0
- package/docs/user-facing/array-merge-semantics.md +344 -0
- package/docs/user-facing/map-merge-semantics.md +128 -0
- package/docs/user-facing/merge-semantics.md +7 -3
- package/docs/user-facing/object-merge-semantics.md +77 -0
- package/lib/core/forest/forest.d.ts +5 -1
- package/lib/core/forest/forest.d.ts.map +1 -1
- package/lib/core/forest/forest.js.map +1 -1
- package/lib/core/index.d.ts +1 -1
- package/lib/core/index.d.ts.map +1 -1
- package/lib/core/index.js.map +1 -1
- package/lib/core/schema-stored/storedSchemaRepository.d.ts +7 -3
- package/lib/core/schema-stored/storedSchemaRepository.d.ts.map +1 -1
- package/lib/core/schema-stored/storedSchemaRepository.js +4 -6
- package/lib/core/schema-stored/storedSchemaRepository.js.map +1 -1
- package/lib/core/tree/anchorSet.d.ts +8 -5
- package/lib/core/tree/anchorSet.d.ts.map +1 -1
- package/lib/core/tree/anchorSet.js +12 -11
- package/lib/core/tree/anchorSet.js.map +1 -1
- package/lib/events/emitter.d.ts +21 -9
- package/lib/events/emitter.d.ts.map +1 -1
- package/lib/events/emitter.js +37 -22
- package/lib/events/emitter.js.map +1 -1
- package/lib/events/listeners.d.ts +16 -5
- package/lib/events/listeners.d.ts.map +1 -1
- package/lib/events/listeners.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/basicChunk.d.ts +26 -5
- package/lib/feature-libraries/chunked-forest/basicChunk.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/basicChunk.js +15 -5
- package/lib/feature-libraries/chunked-forest/basicChunk.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkTree.js +1 -1
- package/lib/feature-libraries/chunked-forest/chunkTree.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts +3 -2
- package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.js +19 -9
- package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
- package/lib/feature-libraries/flex-tree/context.d.ts +3 -2
- package/lib/feature-libraries/flex-tree/context.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/context.js +3 -3
- package/lib/feature-libraries/flex-tree/context.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyField.js +1 -1
- package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyNode.js +1 -1
- package/lib/feature-libraries/flex-tree/lazyNode.js.map +1 -1
- package/lib/feature-libraries/flex-tree/utilities.js +1 -1
- package/lib/feature-libraries/flex-tree/utilities.js.map +1 -1
- package/lib/feature-libraries/index.d.ts +1 -1
- package/lib/feature-libraries/index.d.ts.map +1 -1
- package/lib/feature-libraries/index.js +1 -1
- package/lib/feature-libraries/index.js.map +1 -1
- package/lib/feature-libraries/modular-schema/comparison.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/comparison.js +3 -0
- package/lib/feature-libraries/modular-schema/comparison.js.map +1 -1
- package/lib/feature-libraries/modular-schema/discrepancies.d.ts +29 -29
- package/lib/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/discrepancies.js +120 -74
- package/lib/feature-libraries/modular-schema/discrepancies.js.map +1 -1
- package/lib/feature-libraries/modular-schema/genericFieldKind.js +2 -2
- package/lib/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
- package/lib/feature-libraries/modular-schema/index.d.ts +1 -1
- package/lib/feature-libraries/modular-schema/index.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/index.js +1 -1
- package/lib/feature-libraries/modular-schema/index.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFamily.js +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
- package/lib/feature-libraries/object-forest/objectForest.d.ts +2 -2
- package/lib/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
- package/lib/feature-libraries/object-forest/objectForest.js +6 -8
- package/lib/feature-libraries/object-forest/objectForest.js.map +1 -1
- package/lib/feature-libraries/schema-index/schemaSummarizer.js +1 -1
- package/lib/feature-libraries/schema-index/schemaSummarizer.js.map +1 -1
- package/lib/feature-libraries/sequence-field/compose.js +2 -2
- package/lib/feature-libraries/sequence-field/compose.js.map +1 -1
- package/lib/feature-libraries/sequence-field/markListFactory.js +1 -1
- package/lib/feature-libraries/sequence-field/markListFactory.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/schematizingTreeView.js +2 -2
- package/lib/shared-tree/schematizingTreeView.js.map +1 -1
- package/lib/shared-tree/treeApi.js +7 -4
- package/lib/shared-tree/treeApi.js.map +1 -1
- package/lib/shared-tree/treeCheckout.js +7 -7
- package/lib/shared-tree/treeCheckout.js.map +1 -1
- package/lib/shared-tree-core/branch.d.ts +7 -7
- package/lib/shared-tree-core/branch.d.ts.map +1 -1
- package/lib/shared-tree-core/branch.js +36 -26
- package/lib/shared-tree-core/branch.js.map +1 -1
- package/lib/shared-tree-core/editManager.js +4 -4
- package/lib/shared-tree-core/editManager.js.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.js +5 -5
- package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/lib/simple-tree/api/schemaCreationUtilities.d.ts +12 -14
- package/lib/simple-tree/api/schemaCreationUtilities.d.ts.map +1 -1
- package/lib/simple-tree/api/schemaCreationUtilities.js +9 -7
- package/lib/simple-tree/api/schemaCreationUtilities.js.map +1 -1
- package/lib/simple-tree/api/schemaFactory.d.ts +68 -10
- package/lib/simple-tree/api/schemaFactory.d.ts.map +1 -1
- package/lib/simple-tree/api/schemaFactory.js +38 -10
- package/lib/simple-tree/api/schemaFactory.js.map +1 -1
- package/lib/simple-tree/api/schemaFactoryRecursive.js.map +1 -1
- package/lib/simple-tree/api/treeNodeApi.js +4 -4
- package/lib/simple-tree/api/treeNodeApi.js.map +1 -1
- package/lib/simple-tree/arrayNode.js +1 -1
- package/lib/simple-tree/arrayNode.js.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.d.ts +7 -8
- package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.js +69 -74
- package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/lib/simple-tree/objectNode.d.ts +1 -1
- package/lib/simple-tree/objectNode.js.map +1 -1
- package/lib/simple-tree/objectNodeTypes.d.ts +3 -0
- package/lib/simple-tree/objectNodeTypes.d.ts.map +1 -1
- package/lib/simple-tree/objectNodeTypes.js +3 -1
- package/lib/simple-tree/objectNodeTypes.js.map +1 -1
- package/lib/simple-tree/proxies.js +1 -1
- package/lib/simple-tree/proxies.js.map +1 -1
- package/lib/simple-tree/schemaTypes.d.ts +26 -1
- package/lib/simple-tree/schemaTypes.d.ts.map +1 -1
- package/lib/simple-tree/schemaTypes.js.map +1 -1
- package/lib/simple-tree/treeNodeValid.js +2 -2
- package/lib/simple-tree/treeNodeValid.js.map +1 -1
- package/lib/util/nestedMap.d.ts.map +1 -1
- package/lib/util/nestedMap.js.map +1 -1
- package/package.json +20 -20
- package/src/core/forest/forest.ts +6 -1
- package/src/core/index.ts +1 -1
- package/src/core/schema-stored/storedSchemaRepository.ts +10 -13
- package/src/core/tree/anchorSet.ts +13 -20
- package/src/events/emitter.ts +45 -24
- package/src/events/listeners.ts +17 -5
- package/src/feature-libraries/chunked-forest/basicChunk.ts +12 -4
- package/src/feature-libraries/chunked-forest/chunkTree.ts +1 -1
- package/src/feature-libraries/chunked-forest/chunkedForest.ts +13 -14
- package/src/feature-libraries/flex-tree/context.ts +5 -7
- package/src/feature-libraries/flex-tree/lazyField.ts +1 -1
- package/src/feature-libraries/flex-tree/lazyNode.ts +1 -1
- package/src/feature-libraries/flex-tree/utilities.ts +1 -1
- package/src/feature-libraries/index.ts +1 -1
- package/src/feature-libraries/modular-schema/comparison.ts +4 -0
- package/src/feature-libraries/modular-schema/discrepancies.ts +188 -124
- package/src/feature-libraries/modular-schema/genericFieldKind.ts +2 -2
- package/src/feature-libraries/modular-schema/index.ts +4 -1
- package/src/feature-libraries/modular-schema/modularChangeFamily.ts +1 -1
- package/src/feature-libraries/object-forest/objectForest.ts +5 -11
- package/src/feature-libraries/schema-index/schemaSummarizer.ts +1 -1
- package/src/feature-libraries/sequence-field/compose.ts +2 -2
- package/src/feature-libraries/sequence-field/markListFactory.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/schematizingTreeView.ts +2 -2
- package/src/shared-tree/treeApi.ts +9 -7
- package/src/shared-tree/treeCheckout.ts +7 -7
- package/src/shared-tree-core/branch.ts +30 -30
- package/src/shared-tree-core/editManager.ts +4 -4
- package/src/shared-tree-core/sharedTreeCore.ts +5 -5
- package/src/simple-tree/api/schemaCreationUtilities.ts +29 -17
- package/src/simple-tree/api/schemaFactory.ts +62 -29
- package/src/simple-tree/api/schemaFactoryRecursive.ts +1 -1
- package/src/simple-tree/api/treeNodeApi.ts +4 -4
- package/src/simple-tree/arrayNode.ts +1 -1
- package/src/simple-tree/core/treeNodeKernel.ts +68 -72
- package/src/simple-tree/objectNode.ts +1 -1
- package/src/simple-tree/objectNodeTypes.ts +3 -1
- package/src/simple-tree/proxies.ts +1 -1
- package/src/simple-tree/schemaTypes.ts +26 -1
- package/src/simple-tree/treeNodeValid.ts +2 -2
- package/src/util/nestedMap.ts +1 -0
|
@@ -52,7 +52,7 @@ export class SchemaSummarizer implements Summarizable {
|
|
|
52
52
|
collabWindow: CollabWindow,
|
|
53
53
|
) {
|
|
54
54
|
this.codec = makeSchemaCodec(options);
|
|
55
|
-
this.schema.on("afterSchemaChange", () => {
|
|
55
|
+
this.schema.events.on("afterSchemaChange", () => {
|
|
56
56
|
// Invalidate the cache, as we need to regenerate the blob if the schema changes
|
|
57
57
|
// We are assuming that schema changes from remote ops are valid, as we are in a summarization context.
|
|
58
58
|
this.schemaIndexLastChangedSeq = collabWindow.getCurrentSeq();
|
|
@@ -582,7 +582,7 @@ export class ComposeQueue {
|
|
|
582
582
|
}
|
|
583
583
|
}
|
|
584
584
|
|
|
585
|
-
private dequeueBase(length: number =
|
|
585
|
+
private dequeueBase(length: number = Number.POSITIVE_INFINITY): ComposeMarks {
|
|
586
586
|
const baseMark = this.baseMarks.dequeueUpTo(length);
|
|
587
587
|
const movedChanges = getMovedChangesFromMark(this.moveEffects, baseMark);
|
|
588
588
|
if (movedChanges !== undefined) {
|
|
@@ -593,7 +593,7 @@ export class ComposeQueue {
|
|
|
593
593
|
return { baseMark, newMark };
|
|
594
594
|
}
|
|
595
595
|
|
|
596
|
-
private dequeueNew(length: number =
|
|
596
|
+
private dequeueNew(length: number = Number.POSITIVE_INFINITY): ComposeMarks {
|
|
597
597
|
const newMark = this.newMarks.dequeueUpTo(length);
|
|
598
598
|
const baseMark = createNoopMark(newMark.count, undefined, getInputCellId(newMark));
|
|
599
599
|
|
|
@@ -42,7 +42,7 @@ export class MarkListFactory {
|
|
|
42
42
|
if (prev !== undefined && prev.type === mark.type) {
|
|
43
43
|
const merged = tryMergeMarks(prev, mark);
|
|
44
44
|
if (merged !== undefined) {
|
|
45
|
-
this.list.splice(
|
|
45
|
+
this.list.splice(-1, 1, merged);
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
}
|
package/src/packageVersion.ts
CHANGED
|
@@ -297,7 +297,7 @@ export class SchematizingSimpleTreeView<
|
|
|
297
297
|
new HydratedContext(this.rootFieldSchema.allowedTypeSet, view.context),
|
|
298
298
|
);
|
|
299
299
|
|
|
300
|
-
const unregister = this.checkout.storedSchema.on("afterSchemaChange", () => {
|
|
300
|
+
const unregister = this.checkout.storedSchema.events.on("afterSchemaChange", () => {
|
|
301
301
|
unregister();
|
|
302
302
|
this.unregisterCallbacks.delete(unregister);
|
|
303
303
|
view[disposeSymbol]();
|
|
@@ -307,7 +307,7 @@ export class SchematizingSimpleTreeView<
|
|
|
307
307
|
this.view = undefined;
|
|
308
308
|
this.checkout.forest.anchors.slots.delete(SimpleContextSlot);
|
|
309
309
|
|
|
310
|
-
const unregister = this.checkout.storedSchema.on("afterSchemaChange", () => {
|
|
310
|
+
const unregister = this.checkout.storedSchema.events.on("afterSchemaChange", () => {
|
|
311
311
|
unregister();
|
|
312
312
|
this.unregisterCallbacks.delete(unregister);
|
|
313
313
|
this.update();
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { unreachableCase } from "@fluidframework/core-utils/internal";
|
|
7
7
|
import { UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
8
8
|
|
|
9
9
|
import { TreeStatus } from "../feature-libraries/index.js";
|
|
@@ -464,10 +464,12 @@ function runTransactionInCheckout<TResult>(
|
|
|
464
464
|
switch (constraint.type) {
|
|
465
465
|
case "nodeInDocument": {
|
|
466
466
|
const node = getOrCreateInnerNode(constraint.node);
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
467
|
+
const nodeStatus = treeApi.status(constraint.node);
|
|
468
|
+
if (nodeStatus !== TreeStatus.InDocument) {
|
|
469
|
+
throw new UsageError(
|
|
470
|
+
`Attempted to add a "nodeInDocument" constraint, but the node is not currently in the document. Node status: ${nodeStatus}`,
|
|
471
|
+
);
|
|
472
|
+
}
|
|
471
473
|
checkout.editor.addNodeExistsConstraint(node.anchorNode);
|
|
472
474
|
break;
|
|
473
475
|
}
|
|
@@ -478,10 +480,10 @@ function runTransactionInCheckout<TResult>(
|
|
|
478
480
|
let result: ReturnType<typeof transaction>;
|
|
479
481
|
try {
|
|
480
482
|
result = transaction();
|
|
481
|
-
} catch (
|
|
483
|
+
} catch (error) {
|
|
482
484
|
// If the transaction has an unhandled error, abort and rollback the transaction but continue to propagate the error.
|
|
483
485
|
checkout.transaction.abort();
|
|
484
|
-
throw
|
|
486
|
+
throw error;
|
|
485
487
|
}
|
|
486
488
|
|
|
487
489
|
if (result === rollback) {
|
|
@@ -444,15 +444,15 @@ export class TreeCheckout implements ITreeCheckoutFork {
|
|
|
444
444
|
private readonly breaker: Breakable = new Breakable("TreeCheckout"),
|
|
445
445
|
) {
|
|
446
446
|
// when a transaction is started, take a snapshot of the current state of removed roots
|
|
447
|
-
_branch.on("transactionStarted", () => {
|
|
447
|
+
_branch.events.on("transactionStarted", () => {
|
|
448
448
|
this.removedRootsSnapshots.push(this.removedRoots.clone());
|
|
449
449
|
});
|
|
450
450
|
// when a transaction is committed, the latest snapshot of removed roots can be discarded
|
|
451
|
-
_branch.on("transactionCommitted", () => {
|
|
451
|
+
_branch.events.on("transactionCommitted", () => {
|
|
452
452
|
this.removedRootsSnapshots.pop();
|
|
453
453
|
});
|
|
454
454
|
// after a transaction is rolled back, revert removed roots back to the latest snapshot
|
|
455
|
-
_branch.on("transactionRolledBack", () => {
|
|
455
|
+
_branch.events.on("transactionRolledBack", () => {
|
|
456
456
|
const snapshot = this.removedRootsSnapshots.pop();
|
|
457
457
|
assert(snapshot !== undefined, 0x9ae /* a snapshot for removed roots does not exist */);
|
|
458
458
|
this.removedRoots = snapshot;
|
|
@@ -462,7 +462,7 @@ export class TreeCheckout implements ITreeCheckoutFork {
|
|
|
462
462
|
// For example, a bug in the editor might produce a malformed change object and thus applying the change to the forest will throw an error.
|
|
463
463
|
// In such a case we will crash here, preventing the change from being added to the commit graph, and preventing `afterChange` from firing.
|
|
464
464
|
// One important consequence of this is that we will not submit the op containing the invalid change, since op submissions happens in response to `afterChange`.
|
|
465
|
-
_branch.on("beforeChange", (event) => {
|
|
465
|
+
_branch.events.on("beforeChange", (event) => {
|
|
466
466
|
if (event.change !== undefined) {
|
|
467
467
|
const revision =
|
|
468
468
|
event.type === "replace"
|
|
@@ -509,7 +509,7 @@ export class TreeCheckout implements ITreeCheckoutFork {
|
|
|
509
509
|
}
|
|
510
510
|
}
|
|
511
511
|
});
|
|
512
|
-
_branch.on("afterChange", (event) => {
|
|
512
|
+
_branch.events.on("afterChange", (event) => {
|
|
513
513
|
// The following logic allows revertibles to be generated for the change.
|
|
514
514
|
// Currently only appends (including merges) and transaction commits are supported.
|
|
515
515
|
if (!_branch.isTransacting()) {
|
|
@@ -590,7 +590,7 @@ export class TreeCheckout implements ITreeCheckoutFork {
|
|
|
590
590
|
|
|
591
591
|
// When the branch is trimmed, we can garbage collect any repair data whose latest relevant revision is one of the
|
|
592
592
|
// trimmed revisions.
|
|
593
|
-
_branch.on("ancestryTrimmed", (revisions) => {
|
|
593
|
+
_branch.events.on("ancestryTrimmed", (revisions) => {
|
|
594
594
|
this.withCombinedVisitor((visitor) => {
|
|
595
595
|
revisions.forEach((revision) => {
|
|
596
596
|
// get all the roots last created or used by the revision
|
|
@@ -653,7 +653,7 @@ export class TreeCheckout implements ITreeCheckoutFork {
|
|
|
653
653
|
}
|
|
654
654
|
|
|
655
655
|
public get rootEvents(): Listenable<AnchorSetRootEvents> {
|
|
656
|
-
return this.forest.anchors;
|
|
656
|
+
return this.forest.anchors.events;
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
public get editor(): ISharedTreeEditor {
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
tagRollbackInverse,
|
|
22
22
|
type RebaseStatsWithDuration,
|
|
23
23
|
} from "../core/index.js";
|
|
24
|
-
import {
|
|
24
|
+
import { createEmitter, type Listenable } from "../events/index.js";
|
|
25
25
|
|
|
26
26
|
import { TransactionStack } from "./transactionStack.js";
|
|
27
27
|
import { fail } from "../util/index.js";
|
|
@@ -175,10 +175,9 @@ export interface BranchTrimmingEvents {
|
|
|
175
175
|
/**
|
|
176
176
|
* A branch of changes that can be applied to a SharedTree.
|
|
177
177
|
*/
|
|
178
|
-
export class SharedTreeBranch<
|
|
179
|
-
|
|
180
|
-
TChange
|
|
181
|
-
> extends EventEmitter<SharedTreeBranchEvents<TEditor, TChange>> {
|
|
178
|
+
export class SharedTreeBranch<TEditor extends ChangeFamilyEditor, TChange> {
|
|
179
|
+
readonly #events = createEmitter<SharedTreeBranchEvents<TEditor, TChange>>();
|
|
180
|
+
public readonly events: Listenable<SharedTreeBranchEvents<TEditor, TChange>> = this.#events;
|
|
182
181
|
public readonly editor: TEditor;
|
|
183
182
|
private readonly transactions = new TransactionStack();
|
|
184
183
|
/**
|
|
@@ -222,12 +221,11 @@ export class SharedTreeBranch<
|
|
|
222
221
|
keyof RebaseStatsWithDuration
|
|
223
222
|
>,
|
|
224
223
|
) {
|
|
225
|
-
super();
|
|
226
224
|
this.editor = this.changeFamily.buildEditor(mintRevisionTag, (change) =>
|
|
227
225
|
this.apply(change),
|
|
228
226
|
);
|
|
229
227
|
this.unsubscribeBranchTrimmer = branchTrimmer?.on("ancestryTrimmed", (commit) => {
|
|
230
|
-
this.emit("ancestryTrimmed", commit);
|
|
228
|
+
this.#events.emit("ancestryTrimmed", commit);
|
|
231
229
|
});
|
|
232
230
|
}
|
|
233
231
|
|
|
@@ -267,9 +265,9 @@ export class SharedTreeBranch<
|
|
|
267
265
|
newCommits: [newHead],
|
|
268
266
|
} as const;
|
|
269
267
|
|
|
270
|
-
this.emit("beforeChange", changeEvent);
|
|
268
|
+
this.#events.emit("beforeChange", changeEvent);
|
|
271
269
|
this.head = newHead;
|
|
272
|
-
this.emit("afterChange", changeEvent);
|
|
270
|
+
this.#events.emit("afterChange", changeEvent);
|
|
273
271
|
return [taggedChange.change, newHead];
|
|
274
272
|
}
|
|
275
273
|
|
|
@@ -290,7 +288,7 @@ export class SharedTreeBranch<
|
|
|
290
288
|
const onDisposeUnSubscribes: (() => void)[] = [];
|
|
291
289
|
const onForkUnSubscribe = onForkTransitive(this, (fork) => {
|
|
292
290
|
forks.add(fork);
|
|
293
|
-
onDisposeUnSubscribes.push(fork.on("dispose", () => forks.delete(fork)));
|
|
291
|
+
onDisposeUnSubscribes.push(fork.events.on("dispose", () => forks.delete(fork)));
|
|
294
292
|
});
|
|
295
293
|
this.transactions.push(this.head.revision, () => {
|
|
296
294
|
forks.forEach((fork) => fork.dispose());
|
|
@@ -298,7 +296,7 @@ export class SharedTreeBranch<
|
|
|
298
296
|
onForkUnSubscribe();
|
|
299
297
|
});
|
|
300
298
|
this.editor.enterTransaction();
|
|
301
|
-
this.emit("transactionStarted", this.transactions.size === 1);
|
|
299
|
+
this.#events.emit("transactionStarted", this.transactions.size === 1);
|
|
302
300
|
}
|
|
303
301
|
|
|
304
302
|
/**
|
|
@@ -315,7 +313,7 @@ export class SharedTreeBranch<
|
|
|
315
313
|
const [startCommit, commits] = this.popTransaction();
|
|
316
314
|
this.editor.exitTransaction();
|
|
317
315
|
|
|
318
|
-
this.emit("transactionCommitted", this.transactions.size === 0);
|
|
316
|
+
this.#events.emit("transactionCommitted", this.transactions.size === 0);
|
|
319
317
|
if (commits.length === 0) {
|
|
320
318
|
return undefined;
|
|
321
319
|
}
|
|
@@ -336,9 +334,9 @@ export class SharedTreeBranch<
|
|
|
336
334
|
newCommits: [newHead],
|
|
337
335
|
} as const;
|
|
338
336
|
|
|
339
|
-
this.emit("beforeChange", changeEvent);
|
|
337
|
+
this.#events.emit("beforeChange", changeEvent);
|
|
340
338
|
this.head = newHead;
|
|
341
|
-
this.emit("afterChange", changeEvent);
|
|
339
|
+
this.#events.emit("afterChange", changeEvent);
|
|
342
340
|
return [commits, newHead];
|
|
343
341
|
}
|
|
344
342
|
|
|
@@ -356,9 +354,9 @@ export class SharedTreeBranch<
|
|
|
356
354
|
const [startCommit, commits] = this.popTransaction();
|
|
357
355
|
this.editor.exitTransaction();
|
|
358
356
|
|
|
359
|
-
this.emit("transactionAborted", this.transactions.size === 0);
|
|
357
|
+
this.#events.emit("transactionAborted", this.transactions.size === 0);
|
|
360
358
|
if (commits.length === 0) {
|
|
361
|
-
this.emit("transactionRolledBack", this.transactions.size === 0);
|
|
359
|
+
this.#events.emit("transactionRolledBack", this.transactions.size === 0);
|
|
362
360
|
return [undefined, []];
|
|
363
361
|
}
|
|
364
362
|
|
|
@@ -384,10 +382,10 @@ export class SharedTreeBranch<
|
|
|
384
382
|
removedCommits: commits,
|
|
385
383
|
} as const;
|
|
386
384
|
|
|
387
|
-
this.emit("beforeChange", changeEvent);
|
|
385
|
+
this.#events.emit("beforeChange", changeEvent);
|
|
388
386
|
this.head = startCommit;
|
|
389
|
-
this.emit("afterChange", changeEvent);
|
|
390
|
-
this.emit("transactionRolledBack", this.transactions.size === 0);
|
|
387
|
+
this.#events.emit("afterChange", changeEvent);
|
|
388
|
+
this.#events.emit("transactionRolledBack", this.transactions.size === 0);
|
|
391
389
|
return [change, commits];
|
|
392
390
|
}
|
|
393
391
|
|
|
@@ -435,7 +433,7 @@ export class SharedTreeBranch<
|
|
|
435
433
|
this.mintRevisionTag,
|
|
436
434
|
this.branchTrimmer,
|
|
437
435
|
);
|
|
438
|
-
this.emit("fork", fork);
|
|
436
|
+
this.#events.emit("fork", fork);
|
|
439
437
|
return fork;
|
|
440
438
|
}
|
|
441
439
|
|
|
@@ -483,9 +481,9 @@ export class SharedTreeBranch<
|
|
|
483
481
|
newCommits,
|
|
484
482
|
} as const;
|
|
485
483
|
|
|
486
|
-
this.emit("beforeChange", changeEvent);
|
|
484
|
+
this.#events.emit("beforeChange", changeEvent);
|
|
487
485
|
this.head = newSourceHead;
|
|
488
|
-
this.emit("afterChange", changeEvent);
|
|
486
|
+
this.#events.emit("afterChange", changeEvent);
|
|
489
487
|
return rebaseResult;
|
|
490
488
|
}
|
|
491
489
|
|
|
@@ -529,9 +527,9 @@ export class SharedTreeBranch<
|
|
|
529
527
|
newCommits: sourceCommits,
|
|
530
528
|
} as const;
|
|
531
529
|
|
|
532
|
-
this.emit("beforeChange", changeEvent);
|
|
530
|
+
this.#events.emit("beforeChange", changeEvent);
|
|
533
531
|
this.head = rebaseResult.newSourceHead;
|
|
534
|
-
this.emit("afterChange", changeEvent);
|
|
532
|
+
this.#events.emit("afterChange", changeEvent);
|
|
535
533
|
return [change, sourceCommits];
|
|
536
534
|
}
|
|
537
535
|
|
|
@@ -585,7 +583,7 @@ export class SharedTreeBranch<
|
|
|
585
583
|
this.unsubscribeBranchTrimmer?.();
|
|
586
584
|
|
|
587
585
|
this.disposed = true;
|
|
588
|
-
this.emit("dispose");
|
|
586
|
+
this.#events.emit("dispose");
|
|
589
587
|
}
|
|
590
588
|
|
|
591
589
|
private assertNotDisposed(): void {
|
|
@@ -594,20 +592,22 @@ export class SharedTreeBranch<
|
|
|
594
592
|
}
|
|
595
593
|
|
|
596
594
|
/**
|
|
597
|
-
* Registers an event listener that fires when the given
|
|
595
|
+
* Registers an event listener that fires when the given branch forks.
|
|
598
596
|
* The listener will also fire when any of those forks fork, and when those forks of forks fork, and so on.
|
|
599
|
-
* @param
|
|
597
|
+
* @param branch - the branch that will be listened to for forks
|
|
600
598
|
* @param onFork - the fork event listener
|
|
601
599
|
* @returns a function which when called will deregister all registrations (including transitive) created by this function.
|
|
602
600
|
* The deregister function has undefined behavior if called more than once.
|
|
603
601
|
*/
|
|
604
|
-
|
|
605
|
-
|
|
602
|
+
// Branches are invariant over TChange
|
|
603
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
604
|
+
export function onForkTransitive<T extends SharedTreeBranch<ChangeFamilyEditor, any>>(
|
|
605
|
+
branch: T,
|
|
606
606
|
onFork: (fork: T) => void,
|
|
607
607
|
): () => void {
|
|
608
608
|
const offs: (() => void)[] = [];
|
|
609
609
|
offs.push(
|
|
610
|
-
|
|
610
|
+
branch.events.on("fork", (fork: T) => {
|
|
611
611
|
offs.push(onForkTransitive(fork, onFork));
|
|
612
612
|
onFork(fork);
|
|
613
613
|
}),
|
|
@@ -191,7 +191,7 @@ export class EditManager<
|
|
|
191
191
|
this.telemetryEventBatcher,
|
|
192
192
|
);
|
|
193
193
|
|
|
194
|
-
this.localBranch.on("afterChange", (event) => {
|
|
194
|
+
this.localBranch.events.on("afterChange", (event) => {
|
|
195
195
|
if (event.type === "append") {
|
|
196
196
|
for (const commit of event.newCommits) {
|
|
197
197
|
this.localCommits.push(commit);
|
|
@@ -223,19 +223,19 @@ export class EditManager<
|
|
|
223
223
|
private registerBranch(branch: SharedTreeBranch<TEditor, TChangeset>): void {
|
|
224
224
|
this.trackBranch(branch);
|
|
225
225
|
// Whenever the branch is rebased, update our record of its base trunk commit
|
|
226
|
-
const offBeforeRebase = branch.on("beforeChange", (args) => {
|
|
226
|
+
const offBeforeRebase = branch.events.on("beforeChange", (args) => {
|
|
227
227
|
if (args.type === "replace" && getChangeReplaceType(args) === "rebase") {
|
|
228
228
|
this.untrackBranch(branch);
|
|
229
229
|
}
|
|
230
230
|
});
|
|
231
|
-
const offAfterRebase = branch.on("afterChange", (args) => {
|
|
231
|
+
const offAfterRebase = branch.events.on("afterChange", (args) => {
|
|
232
232
|
if (args.type === "replace" && getChangeReplaceType(args) === "rebase") {
|
|
233
233
|
this.trackBranch(branch);
|
|
234
234
|
this.trimTrunk();
|
|
235
235
|
}
|
|
236
236
|
});
|
|
237
237
|
// When the branch is disposed, update our branch set and trim the trunk
|
|
238
|
-
const offDispose = branch.on("dispose", () => {
|
|
238
|
+
const offDispose = branch.events.on("dispose", () => {
|
|
239
239
|
this.untrackBranch(branch);
|
|
240
240
|
this.trimTrunk();
|
|
241
241
|
offBeforeRebase();
|
|
@@ -183,16 +183,16 @@ export class SharedTreeCore<TEditor extends ChangeFamilyEditor, TChange>
|
|
|
183
183
|
this.mintRevisionTag,
|
|
184
184
|
rebaseLogger,
|
|
185
185
|
);
|
|
186
|
-
this.editManager.localBranch.on("transactionStarted", () => {
|
|
186
|
+
this.editManager.localBranch.events.on("transactionStarted", () => {
|
|
187
187
|
this.commitEnricher.startNewTransaction();
|
|
188
188
|
});
|
|
189
|
-
this.editManager.localBranch.on("transactionAborted", () => {
|
|
189
|
+
this.editManager.localBranch.events.on("transactionAborted", () => {
|
|
190
190
|
this.commitEnricher.abortCurrentTransaction();
|
|
191
191
|
});
|
|
192
|
-
this.editManager.localBranch.on("transactionCommitted", () => {
|
|
192
|
+
this.editManager.localBranch.events.on("transactionCommitted", () => {
|
|
193
193
|
this.commitEnricher.commitCurrentTransaction();
|
|
194
194
|
});
|
|
195
|
-
this.editManager.localBranch.on("beforeChange", (change) => {
|
|
195
|
+
this.editManager.localBranch.events.on("beforeChange", (change) => {
|
|
196
196
|
// Ensure that any previously prepared commits that have not been sent are purged.
|
|
197
197
|
this.commitEnricher.purgePreparedCommits();
|
|
198
198
|
if (this.detachedRevision !== undefined) {
|
|
@@ -219,7 +219,7 @@ export class SharedTreeCore<TEditor extends ChangeFamilyEditor, TChange>
|
|
|
219
219
|
this.commitEnricher.prepareCommit(change.newCommits[0] ?? oob(), true);
|
|
220
220
|
}
|
|
221
221
|
});
|
|
222
|
-
this.editManager.localBranch.on("afterChange", (change) => {
|
|
222
|
+
this.editManager.localBranch.events.on("afterChange", (change) => {
|
|
223
223
|
if (this.getLocalBranch().isTransacting()) {
|
|
224
224
|
// We do not submit ops for changes that are part of a transaction.
|
|
225
225
|
return;
|
|
@@ -73,8 +73,8 @@ export function singletonSchema<TScope extends string, TName extends string | nu
|
|
|
73
73
|
/**
|
|
74
74
|
* Converts an enum into a collection of schema which can be used in a union.
|
|
75
75
|
* @remarks
|
|
76
|
-
* Currently only supports `string` enums.
|
|
77
76
|
* The string value of the enum is used as the name of the schema: callers must ensure that it is stable and unique.
|
|
77
|
+
* Numeric enums values have the value implicitly converted into a string.
|
|
78
78
|
* Consider making a dedicated schema factory with a nested scope to avoid the enum members colliding with other schema.
|
|
79
79
|
* @example
|
|
80
80
|
* ```typescript
|
|
@@ -87,7 +87,7 @@ export function singletonSchema<TScope extends string, TName extends string | nu
|
|
|
87
87
|
* // Define the schema for each member of the enum using a nested scope to group them together.
|
|
88
88
|
* const ModeNodes = adaptEnum(new SchemaFactory(`${schemaFactory.scope}.Mode`), Mode);
|
|
89
89
|
* // Defined the types of the nodes which correspond to this the schema.
|
|
90
|
-
* type ModeNodes =
|
|
90
|
+
* type ModeNodes = TreeNodeFromImplicitAllowedTypes<(typeof ModeNodes.schema)>;
|
|
91
91
|
* // An example schema which has an enum as a child.
|
|
92
92
|
* class Parent extends schemaFactory.object("Parent", {
|
|
93
93
|
* // adaptEnum's return value has a ".schema" property can be use as an `AllowedTypes` array allowing any of the members of the enum.
|
|
@@ -106,8 +106,6 @@ export function singletonSchema<TScope extends string, TName extends string | nu
|
|
|
106
106
|
* }
|
|
107
107
|
* ```
|
|
108
108
|
* @privateRemarks
|
|
109
|
-
* TODO:
|
|
110
|
-
* Extend this to support numeric enums.
|
|
111
109
|
* Maybe provide `SchemaFactory.nested` to ease creating nested scopes?
|
|
112
110
|
* @see {@link enumFromStrings} for a similar function that works on arrays of strings instead of an enum.
|
|
113
111
|
* @alpha
|
|
@@ -139,9 +137,12 @@ export function adaptEnum<
|
|
|
139
137
|
|
|
140
138
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
141
139
|
const factoryOut = <TValue extends Values>(value: TValue) => {
|
|
142
|
-
return new out[
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
return new out[
|
|
141
|
+
inverse.get(value) ?? fail("missing enum value")
|
|
142
|
+
// "extends unknown" is required here to handle when TValue is an union: each member of the union should be processed independently.
|
|
143
|
+
]() as TValue extends unknown
|
|
144
|
+
? NodeFromSchema<ReturnType<typeof singletonSchema<TScope, TValue>>>
|
|
145
|
+
: never;
|
|
145
146
|
};
|
|
146
147
|
const out = factoryOut as typeof factoryOut & TOut & { readonly schema: SchemaArray };
|
|
147
148
|
for (const [key, value] of Object.entries(members)) {
|
|
@@ -176,7 +177,7 @@ export function adaptEnum<
|
|
|
176
177
|
* ```typescript
|
|
177
178
|
* const schemaFactory = new SchemaFactory("com.myApp");
|
|
178
179
|
* const Mode = enumFromStrings(schemaFactory, ["Fun", "Cool"]);
|
|
179
|
-
* type Mode =
|
|
180
|
+
* type Mode = TreeNodeFromImplicitAllowedTypes<typeof Mode.schema>;
|
|
180
181
|
* const nodeFromString: Mode = Mode("Fun");
|
|
181
182
|
* const nodeFromSchema: Mode = new Mode.Fun();
|
|
182
183
|
*
|
|
@@ -198,21 +199,32 @@ export function enumFromStrings<
|
|
|
198
199
|
throw new UsageError("All members of enums must have distinct names");
|
|
199
200
|
}
|
|
200
201
|
|
|
201
|
-
type
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
202
|
+
type MembersUnion = Members[number];
|
|
203
|
+
|
|
204
|
+
// Get all keys of the Members tuple which are numeric strings as union of numbers:
|
|
205
|
+
type Indexes = Extract<keyof Members, `${number}`> extends `${infer N extends number}`
|
|
206
|
+
? N
|
|
207
|
+
: never;
|
|
208
|
+
|
|
209
|
+
type TOut = {
|
|
210
|
+
[Index in Indexes as Members[Index]]: ReturnType<
|
|
211
|
+
typeof singletonSchema<TScope, Members[Index] & string>
|
|
209
212
|
>;
|
|
210
213
|
};
|
|
211
214
|
|
|
212
|
-
|
|
215
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
216
|
+
const factoryOut = <TValue extends MembersUnion>(value: TValue) => {
|
|
217
|
+
// "extends unknown" is required here to handle when TValue is an union: each member of the union should be processed independently.
|
|
218
|
+
return new recordOut[value]() as TValue extends unknown
|
|
219
|
+
? NodeFromSchema<ReturnType<typeof singletonSchema<TScope, TValue>>>
|
|
220
|
+
: never;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
type SchemaArray = UnionToTuple<MembersUnion extends unknown ? TOut[MembersUnion] : never>;
|
|
213
224
|
const schemaArray: TreeNodeSchema[] = [];
|
|
214
225
|
|
|
215
226
|
const out = factoryOut as typeof factoryOut & TOut & { readonly schema: SchemaArray };
|
|
227
|
+
const recordOut = out as Record<MembersUnion, new () => unknown>;
|
|
216
228
|
for (const name of members) {
|
|
217
229
|
const schema = singletonSchema(factory, name);
|
|
218
230
|
schemaArray.push(schema);
|
|
@@ -216,19 +216,45 @@ export class SchemaFactory<
|
|
|
216
216
|
private readonly structuralTypes: Map<string, TreeNodeSchema> = new Map();
|
|
217
217
|
|
|
218
218
|
/**
|
|
219
|
-
* Construct a SchemaFactory with a given scope.
|
|
219
|
+
* Construct a SchemaFactory with a given {@link SchemaFactory.scope|scope}.
|
|
220
220
|
* @remarks
|
|
221
|
-
* There are no restrictions on mixing schema from different schema factories
|
|
222
|
-
*
|
|
223
|
-
* If each library exporting schema picks its own globally unique scope for its SchemaFactory,
|
|
224
|
-
* then all schema an application might depend on, directly or transitively,
|
|
225
|
-
* will end up with a unique fully qualified name which is required to refer to it in persisted data and errors.
|
|
226
|
-
*
|
|
227
|
-
* @param scope - Prefix appended to the identifiers of all {@link TreeNodeSchema} produced by this builder.
|
|
228
|
-
* Use of [Reverse domain name notation](https://en.wikipedia.org/wiki/Reverse_domain_name_notation) or a UUIDv4 is recommended to avoid collisions.
|
|
229
|
-
* You may opt out of using a scope by passing `undefined`, but note that this increases the risk of collisions.
|
|
221
|
+
* There are no restrictions on mixing schema from different schema factories.
|
|
222
|
+
* Typically each library will create one or more SchemaFactories and use them to define its schema.
|
|
230
223
|
*/
|
|
231
|
-
public constructor(
|
|
224
|
+
public constructor(
|
|
225
|
+
/**
|
|
226
|
+
* Prefix appended to the identifiers of all {@link TreeNodeSchema} produced by this builder.
|
|
227
|
+
*
|
|
228
|
+
* @remarks
|
|
229
|
+
* Generally each independently developed library
|
|
230
|
+
* (possibly a package, but could also be part of a package or multiple packages developed together)
|
|
231
|
+
* should get its own unique `scope`.
|
|
232
|
+
* Then each schema in the library get a name which is unique within the library.
|
|
233
|
+
* The scope and name are joined (with a period) to form the {@link TreeNodeSchemaCore.identifier|schema identifier}.
|
|
234
|
+
* Following this pattern allows a single application to depend on multiple libraries which define their own schema, and use them together in a single tree without risk of collisions.
|
|
235
|
+
* If a library logically contains sub-libraries with their own schema, they can be given a scope nested inside the parent scope, such as "ParentScope.ChildScope".
|
|
236
|
+
*
|
|
237
|
+
* To avoid collisions between the scopes of libraries
|
|
238
|
+
* it is recommended that the libraries use {@link https://en.wikipedia.org/wiki/Reverse_domain_name_notation | Reverse domain name notation} or a UUIDv4 for their scope.
|
|
239
|
+
* If this pattern is followed, application can safely use third party libraries without risk of the schema in them colliding.
|
|
240
|
+
*
|
|
241
|
+
* You may opt out of using a scope by passing `undefined`, but note that this increases the risk of collisions.
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* Fluid Framework follows this pattern, placing the schema for the built in leaf types in the `com.fluidframework.leaf` scope.
|
|
245
|
+
* If Fluid Framework publishes more schema in the future, they would be under some other `com.fluidframework` scope.
|
|
246
|
+
* This ensures that any schema defined by any other library will not conflict with Fluid Framework's schema
|
|
247
|
+
* as long as the library uses the recommended patterns for how to scope its schema..
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* A library could generate a random UUIDv4, like `242c4397-49ed-47e6-8dd0-d5c3bc31778b` and use that as the scope.
|
|
251
|
+
* Note: do not use this UUID: a new one must be randomly generated when needed to ensure collision resistance.
|
|
252
|
+
* ```typescript
|
|
253
|
+
* const factory = new SchemaFactory("242c4397-49ed-47e6-8dd0-d5c3bc31778b");
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
public readonly scope: TScope,
|
|
257
|
+
) {}
|
|
232
258
|
|
|
233
259
|
private scoped<Name extends TName | string>(name: Name): ScopedSchemaName<TScope, Name> {
|
|
234
260
|
return (
|
|
@@ -778,6 +804,8 @@ export class SchemaFactory<
|
|
|
778
804
|
name,
|
|
779
805
|
allowedTypes as T & ImplicitAllowedTypes,
|
|
780
806
|
true,
|
|
807
|
+
// Setting this (implicitlyConstructable) to true seems to work ok currently, but not for other node kinds.
|
|
808
|
+
// Supporting this could be fragile and might break other future changes, so it's being kept as false for now.
|
|
781
809
|
false,
|
|
782
810
|
);
|
|
783
811
|
|
|
@@ -785,24 +813,29 @@ export class SchemaFactory<
|
|
|
785
813
|
ScopedSchemaName<TScope, Name>,
|
|
786
814
|
NodeKind.Map,
|
|
787
815
|
TreeMapNodeUnsafe<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Map>,
|
|
788
|
-
{
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
// Ideally this would be
|
|
805
|
-
//
|
|
816
|
+
| {
|
|
817
|
+
/**
|
|
818
|
+
* Iterator for the iterable of content for this node.
|
|
819
|
+
* @privateRemarks
|
|
820
|
+
* Wrapping the constructor parameter for recursive arrays and maps in an inlined object type avoids (for unknown reasons)
|
|
821
|
+
* the following compile error when declaring the recursive schema:
|
|
822
|
+
* `Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.`
|
|
823
|
+
* To benefit from this without impacting the API, the definition of `Iterable` has been inlined as such an object.
|
|
824
|
+
*
|
|
825
|
+
* If this workaround is kept, ideally this comment would be deduplicated with the other instance of it.
|
|
826
|
+
* Unfortunately attempts to do this failed to avoid the compile error this was introduced to solve.
|
|
827
|
+
*/
|
|
828
|
+
[Symbol.iterator](): Iterator<
|
|
829
|
+
[string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>]
|
|
830
|
+
>;
|
|
831
|
+
}
|
|
832
|
+
// Ideally this would be
|
|
833
|
+
// RestrictiveStringRecord<InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>>,
|
|
834
|
+
// but doing so breaks recursive types.
|
|
835
|
+
// Instead we do a less nice version:
|
|
836
|
+
| {
|
|
837
|
+
readonly [P in string]: InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>;
|
|
838
|
+
},
|
|
806
839
|
false,
|
|
807
840
|
T,
|
|
808
841
|
undefined
|
|
@@ -140,7 +140,7 @@ export type ValidateRecursiveSchema<
|
|
|
140
140
|
? Iterable<[string, InsertableTreeNodeFromImplicitAllowedTypes<T["info"]>]>
|
|
141
141
|
: unknown;
|
|
142
142
|
}[T["kind"]],
|
|
143
|
-
// ImplicitlyConstructable: recursive types are not implicitly constructable.
|
|
143
|
+
// ImplicitlyConstructable: recursive types are currently not implicitly constructable.
|
|
144
144
|
false,
|
|
145
145
|
// Info: What's passed to the method to create the schema. Constraining these here should be about as effective as if the actual constraints existed on the actual method itself.
|
|
146
146
|
{
|
|
@@ -168,7 +168,7 @@ export const treeNodeApi: TreeNodeApi = {
|
|
|
168
168
|
case "nodeChanged": {
|
|
169
169
|
const nodeSchema = kernel.schema;
|
|
170
170
|
if (isObjectNodeSchema(nodeSchema)) {
|
|
171
|
-
return kernel.on("childrenChangedAfterBatch", ({ changedFields }) => {
|
|
171
|
+
return kernel.events.on("childrenChangedAfterBatch", ({ changedFields }) => {
|
|
172
172
|
const changedProperties = new Set(
|
|
173
173
|
Array.from(
|
|
174
174
|
changedFields,
|
|
@@ -180,17 +180,17 @@ export const treeNodeApi: TreeNodeApi = {
|
|
|
180
180
|
listener({ changedProperties });
|
|
181
181
|
});
|
|
182
182
|
} else if (nodeSchema.kind === NodeKind.Array) {
|
|
183
|
-
return kernel.on("childrenChangedAfterBatch", () => {
|
|
183
|
+
return kernel.events.on("childrenChangedAfterBatch", () => {
|
|
184
184
|
listener({ changedProperties: undefined });
|
|
185
185
|
});
|
|
186
186
|
} else {
|
|
187
|
-
return kernel.on("childrenChangedAfterBatch", ({ changedFields }) => {
|
|
187
|
+
return kernel.events.on("childrenChangedAfterBatch", ({ changedFields }) => {
|
|
188
188
|
listener({ changedProperties: changedFields });
|
|
189
189
|
});
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
case "treeChanged": {
|
|
193
|
-
return kernel.on("subtreeChangedAfterBatch", () => listener({}));
|
|
193
|
+
return kernel.events.on("subtreeChangedAfterBatch", () => listener({}));
|
|
194
194
|
}
|
|
195
195
|
default:
|
|
196
196
|
throw new UsageError(`No event named ${JSON.stringify(eventName)}.`);
|