@fluidframework/tree 2.114.0 → 2.116.0-416006
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 +191 -0
- package/api-report/tree.alpha.api.md +108 -39
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/rebase/index.d.ts +1 -1
- package/dist/core/rebase/index.d.ts.map +1 -1
- package/dist/core/rebase/index.js +2 -1
- package/dist/core/rebase/index.js.map +1 -1
- package/dist/core/rebase/types.d.ts +110 -0
- package/dist/core/rebase/types.d.ts.map +1 -1
- package/dist/core/rebase/types.js +47 -1
- package/dist/core/rebase/types.js.map +1 -1
- package/dist/entrypoints/alpha.d.ts +1 -1
- package/dist/entrypoints/alpha.d.ts.map +1 -1
- package/dist/entrypoints/alpha.js +6 -4
- package/dist/entrypoints/alpha.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts +9 -6
- package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/codecs.js +16 -13
- package/dist/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/shared-tree/sharedTree.d.ts +5 -0
- package/dist/shared-tree/sharedTree.d.ts.map +1 -1
- package/dist/shared-tree/sharedTree.js +1 -1
- package/dist/shared-tree/sharedTree.js.map +1 -1
- package/dist/shared-tree/treeCheckout.d.ts +5 -0
- package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
- package/dist/shared-tree/treeCheckout.js +58 -12
- package/dist/shared-tree/treeCheckout.js.map +1 -1
- package/dist/shared-tree-core/branch.d.ts +1 -0
- package/dist/shared-tree-core/branch.d.ts.map +1 -1
- package/dist/shared-tree-core/branch.js.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.js +1 -1
- package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/dist/simple-tree/api/index.d.ts +1 -1
- package/dist/simple-tree/api/index.d.ts.map +1 -1
- package/dist/simple-tree/api/index.js +2 -3
- package/dist/simple-tree/api/index.js.map +1 -1
- package/dist/simple-tree/api/schemaFactoryAlpha.d.ts +3 -3
- package/dist/simple-tree/api/schemaFactoryAlpha.d.ts.map +1 -1
- package/dist/simple-tree/api/schemaStatics.d.ts +6 -7
- package/dist/simple-tree/api/schemaStatics.d.ts.map +1 -1
- package/dist/simple-tree/api/schemaStatics.js.map +1 -1
- package/dist/simple-tree/api/treeNodeApi.d.ts +20 -24
- package/dist/simple-tree/api/treeNodeApi.d.ts.map +1 -1
- package/dist/simple-tree/api/treeNodeApi.js +48 -49
- package/dist/simple-tree/api/treeNodeApi.js.map +1 -1
- package/dist/simple-tree/core/treeNode.d.ts +5 -6
- package/dist/simple-tree/core/treeNode.d.ts.map +1 -1
- package/dist/simple-tree/core/treeNode.js +2 -4
- package/dist/simple-tree/core/treeNode.js.map +1 -1
- package/dist/simple-tree/core/withType.d.ts +20 -7
- package/dist/simple-tree/core/withType.d.ts.map +1 -1
- package/dist/simple-tree/core/withType.js +4 -7
- package/dist/simple-tree/core/withType.js.map +1 -1
- package/dist/simple-tree/index.d.ts +2 -2
- package/dist/simple-tree/index.d.ts.map +1 -1
- package/dist/simple-tree/index.js +3 -4
- package/dist/simple-tree/index.js.map +1 -1
- package/dist/simple-tree/leafNodeSchema.d.ts +6 -1
- package/dist/simple-tree/leafNodeSchema.d.ts.map +1 -1
- package/dist/simple-tree/leafNodeSchema.js +0 -1
- package/dist/simple-tree/leafNodeSchema.js.map +1 -1
- package/dist/simple-tree/node-kinds/array/arrayNode.d.ts +32 -3
- package/dist/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
- package/dist/simple-tree/node-kinds/array/arrayNode.js +91 -10
- package/dist/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
- package/dist/simple-tree/node-kinds/map/mapNode.d.ts +77 -0
- package/dist/simple-tree/node-kinds/map/mapNode.d.ts.map +1 -1
- package/dist/simple-tree/node-kinds/map/mapNode.js +17 -1
- package/dist/simple-tree/node-kinds/map/mapNode.js.map +1 -1
- package/dist/simple-tree/node-kinds/object/objectNode.js +2 -2
- package/dist/simple-tree/node-kinds/object/objectNode.js.map +1 -1
- package/dist/simple-tree/node-kinds/record/recordNode.js +2 -2
- package/dist/simple-tree/node-kinds/record/recordNode.js.map +1 -1
- package/dist/text/codePointUtils.d.ts +2 -2
- package/dist/text/codePointUtils.js +2 -2
- package/dist/text/codePointUtils.js.map +1 -1
- package/dist/text/index.d.ts +3 -3
- package/dist/text/index.d.ts.map +1 -1
- package/dist/text/index.js +4 -4
- package/dist/text/index.js.map +1 -1
- package/dist/text/textDomain.d.ts +18 -18
- package/dist/text/textDomain.d.ts.map +1 -1
- package/dist/text/textDomain.js +8 -8
- package/dist/text/textDomain.js.map +1 -1
- package/dist/text/textDomainFormatted.d.ts +46 -42
- package/dist/text/textDomainFormatted.d.ts.map +1 -1
- package/dist/text/textDomainFormatted.js +32 -28
- package/dist/text/textDomainFormatted.js.map +1 -1
- package/dist/text/textDomainFormattedDefault.d.ts +18 -18
- package/dist/text/textDomainFormattedDefault.d.ts.map +1 -1
- package/dist/text/textDomainFormattedDefault.js +14 -17
- package/dist/text/textDomainFormattedDefault.js.map +1 -1
- package/dist/util/compressedIds.d.ts +22 -2
- package/dist/util/compressedIds.d.ts.map +1 -1
- package/dist/util/compressedIds.js +22 -1
- package/dist/util/compressedIds.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 +1 -1
- package/lib/core/index.js.map +1 -1
- package/lib/core/rebase/index.d.ts +1 -1
- package/lib/core/rebase/index.d.ts.map +1 -1
- package/lib/core/rebase/index.js +1 -1
- package/lib/core/rebase/index.js.map +1 -1
- package/lib/core/rebase/types.d.ts +110 -0
- package/lib/core/rebase/types.d.ts.map +1 -1
- package/lib/core/rebase/types.js +46 -0
- package/lib/core/rebase/types.js.map +1 -1
- package/lib/entrypoints/alpha.d.ts +1 -1
- package/lib/entrypoints/alpha.d.ts.map +1 -1
- package/lib/entrypoints/alpha.js +1 -1
- package/lib/entrypoints/alpha.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts +9 -6
- package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/codecs.js +16 -13
- package/lib/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/shared-tree/sharedTree.d.ts +5 -0
- package/lib/shared-tree/sharedTree.d.ts.map +1 -1
- package/lib/shared-tree/sharedTree.js +1 -1
- package/lib/shared-tree/sharedTree.js.map +1 -1
- package/lib/shared-tree/treeCheckout.d.ts +5 -0
- package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
- package/lib/shared-tree/treeCheckout.js +60 -14
- package/lib/shared-tree/treeCheckout.js.map +1 -1
- package/lib/shared-tree-core/branch.d.ts +1 -0
- package/lib/shared-tree-core/branch.d.ts.map +1 -1
- package/lib/shared-tree-core/branch.js.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.js +1 -1
- package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/lib/simple-tree/api/index.d.ts +1 -1
- package/lib/simple-tree/api/index.d.ts.map +1 -1
- package/lib/simple-tree/api/index.js +1 -1
- package/lib/simple-tree/api/index.js.map +1 -1
- package/lib/simple-tree/api/schemaFactoryAlpha.d.ts +3 -3
- package/lib/simple-tree/api/schemaFactoryAlpha.d.ts.map +1 -1
- package/lib/simple-tree/api/schemaStatics.d.ts +6 -7
- package/lib/simple-tree/api/schemaStatics.d.ts.map +1 -1
- package/lib/simple-tree/api/schemaStatics.js.map +1 -1
- package/lib/simple-tree/api/treeNodeApi.d.ts +20 -24
- package/lib/simple-tree/api/treeNodeApi.d.ts.map +1 -1
- package/lib/simple-tree/api/treeNodeApi.js +46 -46
- package/lib/simple-tree/api/treeNodeApi.js.map +1 -1
- package/lib/simple-tree/core/treeNode.d.ts +5 -6
- package/lib/simple-tree/core/treeNode.d.ts.map +1 -1
- package/lib/simple-tree/core/treeNode.js +2 -4
- package/lib/simple-tree/core/treeNode.js.map +1 -1
- package/lib/simple-tree/core/withType.d.ts +20 -7
- package/lib/simple-tree/core/withType.d.ts.map +1 -1
- package/lib/simple-tree/core/withType.js +4 -7
- package/lib/simple-tree/core/withType.js.map +1 -1
- package/lib/simple-tree/index.d.ts +2 -2
- package/lib/simple-tree/index.d.ts.map +1 -1
- package/lib/simple-tree/index.js +1 -1
- package/lib/simple-tree/index.js.map +1 -1
- package/lib/simple-tree/leafNodeSchema.d.ts +6 -1
- package/lib/simple-tree/leafNodeSchema.d.ts.map +1 -1
- package/lib/simple-tree/leafNodeSchema.js +0 -1
- package/lib/simple-tree/leafNodeSchema.js.map +1 -1
- package/lib/simple-tree/node-kinds/array/arrayNode.d.ts +32 -3
- package/lib/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
- package/lib/simple-tree/node-kinds/array/arrayNode.js +93 -12
- package/lib/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
- package/lib/simple-tree/node-kinds/map/mapNode.d.ts +77 -0
- package/lib/simple-tree/node-kinds/map/mapNode.d.ts.map +1 -1
- package/lib/simple-tree/node-kinds/map/mapNode.js +18 -2
- package/lib/simple-tree/node-kinds/map/mapNode.js.map +1 -1
- package/lib/simple-tree/node-kinds/object/objectNode.js +3 -3
- package/lib/simple-tree/node-kinds/object/objectNode.js.map +1 -1
- package/lib/simple-tree/node-kinds/record/recordNode.js +3 -3
- package/lib/simple-tree/node-kinds/record/recordNode.js.map +1 -1
- package/lib/text/codePointUtils.d.ts +2 -2
- package/lib/text/codePointUtils.js +2 -2
- package/lib/text/codePointUtils.js.map +1 -1
- package/lib/text/index.d.ts +3 -3
- package/lib/text/index.d.ts.map +1 -1
- package/lib/text/index.js +3 -3
- package/lib/text/index.js.map +1 -1
- package/lib/text/textDomain.d.ts +18 -18
- package/lib/text/textDomain.d.ts.map +1 -1
- package/lib/text/textDomain.js +7 -7
- package/lib/text/textDomain.js.map +1 -1
- package/lib/text/textDomainFormatted.d.ts +46 -42
- package/lib/text/textDomainFormatted.d.ts.map +1 -1
- package/lib/text/textDomainFormatted.js +31 -27
- package/lib/text/textDomainFormatted.js.map +1 -1
- package/lib/text/textDomainFormattedDefault.d.ts +18 -18
- package/lib/text/textDomainFormattedDefault.d.ts.map +1 -1
- package/lib/text/textDomainFormattedDefault.js +14 -17
- package/lib/text/textDomainFormattedDefault.js.map +1 -1
- package/lib/util/compressedIds.d.ts +22 -2
- package/lib/util/compressedIds.d.ts.map +1 -1
- package/lib/util/compressedIds.js +22 -1
- package/lib/util/compressedIds.js.map +1 -1
- package/package.json +29 -22
- package/src/core/index.ts +2 -0
- package/src/core/rebase/index.ts +2 -0
- package/src/core/rebase/types.ts +113 -0
- package/src/entrypoints/alpha.ts +10 -6
- package/src/feature-libraries/chunked-forest/codec/codecs.ts +17 -14
- package/src/index.ts +7 -4
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/sharedTree.ts +6 -1
- package/src/shared-tree/treeCheckout.ts +81 -14
- package/src/shared-tree-core/branch.ts +1 -0
- package/src/shared-tree-core/sharedTreeCore.ts +1 -1
- package/src/simple-tree/api/index.ts +0 -1
- package/src/simple-tree/api/schemaStatics.ts +2 -2
- package/src/simple-tree/api/treeNodeApi.ts +64 -48
- package/src/simple-tree/core/treeNode.ts +13 -10
- package/src/simple-tree/core/withType.ts +20 -7
- package/src/simple-tree/index.ts +1 -2
- package/src/simple-tree/leafNodeSchema.ts +6 -1
- package/src/simple-tree/node-kinds/array/arrayNode.ts +109 -14
- package/src/simple-tree/node-kinds/map/mapNode.ts +113 -3
- package/src/simple-tree/node-kinds/object/objectNode.ts +3 -3
- package/src/simple-tree/node-kinds/record/recordNode.ts +3 -3
- package/src/text/README.md +1 -1
- package/src/text/codePointUtils.ts +2 -2
- package/src/text/index.ts +3 -3
- package/src/text/textDomain.ts +21 -21
- package/src/text/textDomainFormatted.ts +56 -52
- package/src/text/textDomainFormattedDefault.ts +14 -23
- package/src/util/compressedIds.ts +36 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textDomainFormattedDefault.js","sourceRoot":"","sources":["../../src/text/textDomainFormattedDefault.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAM5F,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"textDomainFormattedDefault.js","sourceRoot":"","sources":["../../src/text/textDomainFormattedDefault.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAM5F,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;GAEG;AACH,MAAM,EAAE,GAAG,IAAI,iBAAiB,CAAC,2CAA2C,CAAC,CAAC;AAE9E,MAAM,aAAa,GAAG;IACrB,IAAI,EAAE,KAAK;IACX,MAAM,EAAE,KAAK;IACb,SAAS,EAAE,KAAK;IAChB,IAAI,EAAE,EAAE;IACR,IAAI,EAAE,OAAO;CACJ,CAAC;AAEX;;;;;GAKG;AACH,MAAM,KAAW,oBAAoB,CAqHpC;AArHD,WAAiB,oBAAoB;IAQpC;;;;OAIG;IACH,MAAa,eAAgB,SAAQ,EAAE,CAAC,MAAM,CAAC,iBAAiB,EAAE;QACjE,IAAI,EAAE,aAAa,CAAC,OAAO;QAC3B,MAAM,EAAE,aAAa,CAAC,OAAO;QAC7B,SAAS,EAAE,aAAa,CAAC,OAAO;QAChC,IAAI,EAAE,aAAa,CAAC,MAAM;QAC1B,IAAI,EAAE,aAAa,CAAC,MAAM;KAC1B,CAAC;;IACsB,6BAAa,GAAG,IAAI,eAAe,CAAC,aAAa,CAAC,CAAC;IAP9D,oCAAe,kBAQ3B,CAAA;IAED;;;OAGG;IACU,4BAAO,GAAG,eAAe,CAAC,EAAE,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE;QACnE,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,SAAS;QACT,WAAW;QACX,YAAY;QACZ,WAAW;KACX,CAAC,CAAC;IAQH;;;;;;;;;;;OAWG;IACH,MAAa,cAAe,SAAQ,EAAE,CAAC,MAAM,CAAC,gBAAgB,EAAE;QAC/D,GAAG,EAAE,qBAAA,OAAO,CAAC,MAAM;QACnB,MAAM,EAAE,aAAa,CAAC,MAAM;KAC5B,CAAC;QAHF;;YAIiB,YAAO,GAAG,IAAI,CAAC;QAChC,CAAC;KAAA;IALY,mCAAc,iBAK1B,CAAA;IAED;;;;OAIG;IACU,sCAAiB,GAAG,CAAC,aAAa,CAAC,cAAc,EAAE,cAAc,CAAU,CAAC;IAkCzF;;;;;OAKG;IACH,MAAa,IAAK,SAAQ,aAAa,CAAC,YAAY,CACnD,EAAE,EACF,eAAe,EACf,CAAC,cAAc,CAAC,EAChB,aAAa,CACb;KAAG;IALS,yBAAI,OAKb,CAAA;AACL,CAAC,EArHgB,oBAAoB,KAApB,oBAAoB,QAqHpC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { enumFromStrings, SchemaFactory, SchemaFactoryBeta } from \"../simple-tree/index.js\";\nimport type {\n\tTreeNodeFromImplicitAllowedTypes,\n\tInsertableTreeNodeFromImplicitAllowedTypes,\n} from \"../simple-tree/index.js\";\n\nimport { FormattedText } from \"./textDomainFormatted.js\";\n\n/**\n * Schema factory for default formatted text types which are not generic.\n */\nconst sf = new SchemaFactoryBeta(\"com.fluidframework.text.formatted.default\");\n\nconst defaultFormat = {\n\tbold: false,\n\titalic: false,\n\tunderline: false,\n\tsize: 12,\n\tfont: \"Arial\",\n} as const;\n\n/**\n * A default parameterization of the generic {@link FormattedText} with hard-coded assumptions about what kind of embedded content and what kind of formatting is supported.\n * @remarks\n * It is unlikely this meets the needs of most users, but it can serve as an unstable example of how to use the generic {@link FormattedText}.\n * @internal\n */\nexport namespace FormattedTextDefault {\n\t/**\n\t * Portion of a string with formatting.\n\t * @sealed\n\t * @internal\n\t */\n\texport type FormattedAtom = FormattedText.FormattedAtom<CharacterFormat, StringAtomContent>;\n\n\t/**\n\t * Formatting options for characters.\n\t * @sealed\n\t * @internal\n\t */\n\texport class CharacterFormat extends sf.object(\"CharacterFormat\", {\n\t\tbold: SchemaFactory.boolean,\n\t\titalic: SchemaFactory.boolean,\n\t\tunderline: SchemaFactory.boolean,\n\t\tsize: SchemaFactory.number,\n\t\tfont: SchemaFactory.string,\n\t}) {\n\t\tpublic static readonly defaultFormat = new CharacterFormat(defaultFormat);\n\t}\n\n\t/**\n\t * Tag with which a line in text can be formatted from HTML.\n\t * @internal\n\t */\n\texport const LineTag = enumFromStrings(sf.scopedFactory(\"lineTag\"), [\n\t\t\"h1\",\n\t\t\"h2\",\n\t\t\"h3\",\n\t\t\"h4\",\n\t\t\"h5\",\n\t\t\"li\",\n\t\t\"ol\",\n\t\t\"checked\",\n\t\t\"unchecked\",\n\t\t\"blockquote\",\n\t\t\"codeBlock\",\n\t]);\n\t/**\n\t * {@inheritdoc FormattedTextDefault.(LineTag:variable)}\n\t * @sealed\n\t * @internal\n\t */\n\texport type LineTag = TreeNodeFromImplicitAllowedTypes<typeof LineTag.schema>;\n\n\t/**\n\t * Unit in the string representing a new line character with line formatting.\n\t * @remarks\n\t * This aligns with how Quill represents line formatting.\n\t * Quill formats line attributes (headers, list, blockquote, etc... ) on the newline character\n\t * and only lines using this atom can have line-specific formatting.\n\t * The optional indent level mirrors Quill's indent attribute,\n\t * which is applies to the line before the line break.\n\t * Any tagged line can be indented independently.\n\t * @sealed\n\t * @internal\n\t */\n\texport class StringLineAtom extends sf.object(\"StringLineAtom\", {\n\t\ttag: LineTag.schema,\n\t\tindent: SchemaFactory.number,\n\t}) {\n\t\tpublic readonly content = \"\\n\";\n\t}\n\n\t/**\n\t * Types of \"atoms\" that make up the text.\n\t * @sealed\n\t * @internal\n\t */\n\texport const StringAtomContent = [FormattedText.StringTextAtom, StringLineAtom] as const;\n\t/**\n\t * {@inheritdoc FormattedTextDefault.(StringAtomContent:variable)}\n\t * @sealed\n\t * @internal\n\t */\n\texport type StringAtomContent = TreeNodeFromImplicitAllowedTypes<typeof StringAtomContent>;\n\n\t/**\n\t * Statics for text nodes.\n\t * @sealed\n\t * @internal\n\t */\n\texport type Statics<TTree = Tree> = FormattedText.Statics<TTree, typeof CharacterFormat>;\n\n\t/**\n\t * Insertable shape for a formatted text atom used by {@link FormattedText.Members.insertWithFormattingAt}.\n\t * @sealed\n\t * @internal\n\t */\n\texport type FormattedAtomInsertable = FormattedText.FormattedAtom<\n\t\tInsertableTreeNodeFromImplicitAllowedTypes<typeof CharacterFormat>,\n\t\tInsertableTreeNodeFromImplicitAllowedTypes<TextAtomSchemas>\n\t>;\n\n\t/**\n\t * Helper for expressing the full set of formatted text atoms for a given schema.\n\t * @privateRemarks\n\t * Eventually this should probably be given a better name and/or made a system type in a system namespace.\n\t * @sealed\n\t * @internal\n\t */\n\texport type TextAtomSchemas = FormattedText.TextAtomSchemas<[typeof StringLineAtom]>;\n\n\t/**\n\t * The schema produced using {@link FormattedText.createSchema} with hard-coded assumptions\n\t * about what kind of embedded content and what kind of formatting is supported.\n\t * @sealed\n\t * @internal\n\t */\n\texport class Tree extends FormattedText.createSchema(\n\t\tsf,\n\t\tCharacterFormat,\n\t\t[StringLineAtom],\n\t\tdefaultFormat,\n\t) {}\n}\n"]}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
+
import { type ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
|
|
5
6
|
import type { IIdCompressor, OpSpaceCompressedId, SessionId, SessionSpaceCompressedId } from "@fluidframework/id-compressor";
|
|
6
7
|
/**
|
|
7
8
|
* An encoded identifier that can be decoded without an originator session id.
|
|
@@ -96,6 +97,11 @@ export interface IdentifierHealingConfig {
|
|
|
96
97
|
* same session offsets.
|
|
97
98
|
*/
|
|
98
99
|
readonly sharedObjectId: string;
|
|
100
|
+
/**
|
|
101
|
+
* Optional logger used by {@link forceDecodeEncodedIdWithoutSession} to record telemetry
|
|
102
|
+
* when the heal-on-decode recovery path is taken (a non-final identifier is healed).
|
|
103
|
+
*/
|
|
104
|
+
readonly logger?: ITelemetryBaseLogger;
|
|
99
105
|
}
|
|
100
106
|
/**
|
|
101
107
|
* Decode an op-space compressed id without a session.
|
|
@@ -106,6 +112,16 @@ export interface IdentifierHealingConfig {
|
|
|
106
112
|
* deterministic v5 UUID string — *not* a `StableId`, since that brand requires
|
|
107
113
|
* v4, but still a valid identifier value; the `string` arm of the return type
|
|
108
114
|
* covers this case.
|
|
115
|
+
*
|
|
116
|
+
* The recovery (heal) path only occurs because of a prior bug where non-finalized identifiers were
|
|
117
|
+
* written into summaries. When {@link IdentifierHealingConfig.logger} is supplied, a telemetry event
|
|
118
|
+
* is recorded on that path so heals can be observed in the wild. The error/throw path is intentionally
|
|
119
|
+
* not instrumented here: the thrown exception is expected to surface via the application's own error
|
|
120
|
+
* telemetry, and should never be silently swallowed.
|
|
121
|
+
*
|
|
122
|
+
* @param id - The op-space compressed ID to decode.
|
|
123
|
+
* @param idCompressor - The ID compressor used to normalize the ID.
|
|
124
|
+
* @param healing - Heal-on-decode configuration. Presence enables healing of non-final IDs.
|
|
109
125
|
*/
|
|
110
126
|
export declare function forceDecodeEncodedIdWithoutSession(id: OpSpaceCompressedId, idCompressor: IIdCompressor, healing: IdentifierHealingConfig | undefined): SessionSpaceCompressedId | string;
|
|
111
127
|
/**
|
|
@@ -159,9 +175,13 @@ export interface IdDecoderOptionsWithOriginator {
|
|
|
159
175
|
*/
|
|
160
176
|
export declare class IdDecodingContext {
|
|
161
177
|
/**
|
|
162
|
-
*
|
|
178
|
+
* Whether this context resolves identifiers using an originator session ID.
|
|
179
|
+
* @remarks
|
|
180
|
+
* Consulted by {@link FieldBatchDecodingContext} to prevent using an originator-based
|
|
181
|
+
* decoder in incremental chunks (which may come from other sessions, making such a
|
|
182
|
+
* decoder wrong there).
|
|
163
183
|
*/
|
|
164
|
-
|
|
184
|
+
readonly hasOriginatorSessionId: boolean;
|
|
165
185
|
/**
|
|
166
186
|
* Compressor which can decompress session-space identifiers from {@link resolveEncodedId} as needed.
|
|
167
187
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compressedIds.d.ts","sourceRoot":"","sources":["../../src/util/compressedIds.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"compressedIds.d.ts","sourceRoot":"","sources":["../../src/util/compressedIds.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
+
import { LogLevel } from "@fluidframework/core-interfaces";
|
|
5
6
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
6
7
|
import { isFinalId, isStableId } from "@fluidframework/id-compressor/internal";
|
|
7
8
|
import { v5 as uuidV5 } from "uuid";
|
|
@@ -92,6 +93,16 @@ export function tryDecodeEncodedIdWithoutSession(id, idCompressor) {
|
|
|
92
93
|
* deterministic v5 UUID string — *not* a `StableId`, since that brand requires
|
|
93
94
|
* v4, but still a valid identifier value; the `string` arm of the return type
|
|
94
95
|
* covers this case.
|
|
96
|
+
*
|
|
97
|
+
* The recovery (heal) path only occurs because of a prior bug where non-finalized identifiers were
|
|
98
|
+
* written into summaries. When {@link IdentifierHealingConfig.logger} is supplied, a telemetry event
|
|
99
|
+
* is recorded on that path so heals can be observed in the wild. The error/throw path is intentionally
|
|
100
|
+
* not instrumented here: the thrown exception is expected to surface via the application's own error
|
|
101
|
+
* telemetry, and should never be silently swallowed.
|
|
102
|
+
*
|
|
103
|
+
* @param id - The op-space compressed ID to decode.
|
|
104
|
+
* @param idCompressor - The ID compressor used to normalize the ID.
|
|
105
|
+
* @param healing - Heal-on-decode configuration. Presence enables healing of non-final IDs.
|
|
95
106
|
*/
|
|
96
107
|
export function forceDecodeEncodedIdWithoutSession(id, idCompressor, healing) {
|
|
97
108
|
const decoded = tryDecodeEncodedIdWithoutSession(id, idCompressor);
|
|
@@ -100,7 +111,17 @@ export function forceDecodeEncodedIdWithoutSession(id, idCompressor, healing) {
|
|
|
100
111
|
}
|
|
101
112
|
// `id` is a non-final op-space compressed id.
|
|
102
113
|
if (healing !== undefined) {
|
|
103
|
-
|
|
114
|
+
const healed = uuidV5(`${healing.sharedObjectId}|${id}`, healingNamespace);
|
|
115
|
+
healing.logger?.send({
|
|
116
|
+
category: "generic",
|
|
117
|
+
eventName: "HealUnresolvableIdentifierOnDecode",
|
|
118
|
+
},
|
|
119
|
+
// This telemetry should be very low-volume (and possibly never happen at all),
|
|
120
|
+
// as should only occur when loading documents that were corrupted by a now fixed bug and have not been re-summarized since healing was added.
|
|
121
|
+
// It is useful for monitoring and diagnosing for this to be reported in the same cases in which the error case would be reported,
|
|
122
|
+
// which is the essential level.
|
|
123
|
+
LogLevel.essential);
|
|
124
|
+
return healed;
|
|
104
125
|
}
|
|
105
126
|
throw new Error("Summary could not be loaded due to an incorrectly encoded identifier. See SharedTreeOptionsBeta.healUnresolvableIdentifiersOnDecode for mitigation.");
|
|
106
127
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compressedIds.js","sourceRoot":"","sources":["../../src/util/compressedIds.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type {\n\tIIdCompressor,\n\tOpSpaceCompressedId,\n\tSessionId,\n\tSessionSpaceCompressedId,\n} from \"@fluidframework/id-compressor\";\nimport { isFinalId, isStableId } from \"@fluidframework/id-compressor/internal\";\nimport { v5 as uuidV5 } from \"uuid\";\n\n/**\n * An encoded identifier that can be decoded without an originator session id.\n * A finalized compressed id — the same numeric value in op-space and session-space.\n * @remarks\n * These are safe to use in contexts where the encoding id compressor is available to decode them,\n * even when the session id of the encoding id compressor is not known.\n */\nexport type OriginatorlessEncodedId = SessionSpaceCompressedId & OpSpaceCompressedId;\n\n/**\n * An encoded identifier that may require an originator session id to decode.\n * Any op-space compressed id, either finalized or session-local.\n * @remarks\n * These are only safe to use in contexts where the encoding id compressor is available\n * and the session id of the encoding id compressor is known.\n * Currently the only such case is in ops, excluding attach summary ops.\n */\nexport type OriginatorDependentEncodedId = OpSpaceCompressedId;\n\n/**\n * Selects how identifiers are encoded for transport/persistence.\n */\nexport enum EncodedIdType {\n\t/**\n\t * {@link OriginatorlessEncodedId}.\n\t */\n\tOriginatorless,\n\t/**\n\t * {@link OriginatorDependentEncodedId}.\n\t */\n\tOriginatorDependent,\n}\n\nexport type EncodedId<T extends EncodedIdType> = T extends EncodedIdType.Originatorless\n\t? OriginatorlessEncodedId\n\t: T extends EncodedIdType.OriginatorDependent\n\t\t? OriginatorDependentEncodedId\n\t\t: OriginatorlessEncodedId | OriginatorDependentEncodedId;\n\n/**\n * Context for encoding identifiers.\n * @remarks\n * See {@link FieldBatchDecodingContext} for the decoder.\n */\nexport interface IdEncodingContext {\n\tencodePossiblyCompressedId(id: string): string | EncodedId<EncodedIdType>;\n}\n\n/**\n * Encode a string identifier into a possibly compressed form based on the requested mode.\n *\n * @param id - The string identifier to encode.\n * @param idCompressor - The ID compressor to use for compression.\n * @param encodedIdType - The type of encoding to use. If the context doing the decoding will have the originator {@link SessionId},\n * then {@link EncodedIdType.OriginatorDependent} can be used.\n * Otherwise, {@link EncodedIdType.Originatorless} must be used.\n *\n * @privateRemarks\n * Performance:\n * When trying to optimize to avoid unnecessary identifier decompression, and store identifiers compressed in memory,\n * this will likely need to be updated to allow in SessionSpaceCompressedIds.\n */\nexport function encodePossiblyCompressedId<T extends EncodedIdType>(\n\tid: string,\n\tidCompressor: IIdCompressor,\n\tencodedIdType: T,\n): string | EncodedId<T> {\n\tif (!isStableId(id)) {\n\t\treturn id;\n\t}\n\tconst sessionSpaceCompressedId = idCompressor.tryRecompress(id);\n\tif (sessionSpaceCompressedId === undefined) {\n\t\treturn id;\n\t}\n\tconst opSpaceId = idCompressor.normalizeToOpSpace(sessionSpaceCompressedId);\n\tif (encodedIdType === EncodedIdType.Originatorless) {\n\t\treturn isFinalId(opSpaceId) ? (opSpaceId as unknown as EncodedId<T>) : id;\n\t}\n\treturn opSpaceId as unknown as EncodedId<T>;\n}\n\n/**\n * Namespace used for the deterministic UUIDv5 produced by the heal-on-decode workaround\n * in {@link forceDecodeEncodedIdWithoutSession}.\n *\n * This scheme requires consensus across all clients to function — every client that\n * encounters the same unresolvable id must produce the same v5 UUID.\n */\nconst healingNamespace = \"f8a89df3-6882-400f-b913-4c1f6f0157bd\";\n\n/**\n * Decode an {@link OriginatorlessEncodedId} without needing an originator session id.\n *\n * The id is guaranteed to be final by the type contract; it is normalized via\n * {@link IIdCompressor.tryNormalizeToSessionSpaceWithoutSession}, which handles the\n * cluster-aligned-local case rather than relying on the bare numeric value.\n */\nexport function decodeOriginatorlessEncodedId(\n\tid: OriginatorlessEncodedId,\n\tidCompressor: IIdCompressor,\n): SessionSpaceCompressedId {\n\tconst sessionSpaceId = idCompressor.tryNormalizeToSessionSpaceWithoutSession(id);\n\tassert(\n\t\tsessionSpaceId !== undefined,\n\t\t0xd0a /* OriginatorlessEncodedId must be a finalized compressed id at runtime */,\n\t);\n\treturn sessionSpaceId;\n}\n\n/**\n * Decode an {@link OriginatorDependentEncodedId} using a known originator session id.\n */\nexport function decodeEncodedIdWithOriginator(\n\tid: OriginatorDependentEncodedId,\n\toriginator: SessionId,\n\tidCompressor: IIdCompressor,\n): SessionSpaceCompressedId {\n\treturn idCompressor.normalizeToSessionSpace(id, originator);\n}\n\n/**\n * Decode an {@link OriginatorDependentEncodedId} without a session.\n *\n * Returns the session-space id if `id` is finalized (and therefore resolvable\n * without an originator session), or `undefined` if `id` is a non-final op-space\n * id that cannot be resolved without the originator session.\n */\nexport function tryDecodeEncodedIdWithoutSession(\n\tid: OriginatorDependentEncodedId,\n\tidCompressor: IIdCompressor,\n): SessionSpaceCompressedId | undefined {\n\treturn idCompressor.tryNormalizeToSessionSpaceWithoutSession(id);\n}\n\n/**\n * Configuration for the heal-on-decode workaround. The internal counterpart of\n * the user-facing {@link SharedTreeOptionsBeta.healUnresolvableIdentifiersOnDecode}\n * option — see that option for the user-facing rationale and trade-offs.\n *\n * Carried by decode-side contexts ({@link IdDecoderOptionsOriginatorless},\n * `ChangeEncodingContext.healing`,\n * `EditManagerEncodingContext.healing`, etc.) when the workaround is enabled.\n * Presence enables healing; `undefined` means healing is disabled. There is no separate boolean,\n * which makes it impossible to enable healing without supplying the namespace\n * input.\n */\nexport interface IdentifierHealingConfig {\n\t/**\n\t * The SharedTree's shared-object id, used as the v5 namespace input so\n\t * healed UUIDs are unique across attaches of different documents with the\n\t * same session offsets.\n\t */\n\treadonly sharedObjectId: string;\n}\n\n/**\n * Decode an op-space compressed id without a session.\n *\n * Finalized ids are returned as session-space ids (final ids are invariant\n * across the two spaces). Non-final ids are healed via {@link IdentifierHealingConfig}\n * if provided, or cause the resolver to throw otherwise. A healed result is a\n * deterministic v5 UUID string — *not* a `StableId`, since that brand requires\n * v4, but still a valid identifier value; the `string` arm of the return type\n * covers this case.\n */\nexport function forceDecodeEncodedIdWithoutSession(\n\tid: OpSpaceCompressedId,\n\tidCompressor: IIdCompressor,\n\thealing: IdentifierHealingConfig | undefined,\n): SessionSpaceCompressedId | string {\n\tconst decoded = tryDecodeEncodedIdWithoutSession(id, idCompressor);\n\tif (decoded !== undefined) {\n\t\treturn decoded;\n\t}\n\t// `id` is a non-final op-space compressed id.\n\tif (healing !== undefined) {\n\t\treturn uuidV5(`${healing.sharedObjectId}|${id}`, healingNamespace);\n\t}\n\tthrow new Error(\n\t\t\"Summary could not be loaded due to an incorrectly encoded identifier. See SharedTreeOptionsBeta.healUnresolvableIdentifiersOnDecode for mitigation.\",\n\t);\n}\n\n/**\n * Convert a decoded, but possibly compressed, identifier to the decompressed string format.\n *\n * Session-space compressed ids are decompressed to their UUID representation via the compressor.\n * String inputs pass through unchanged — they are already in the stored form,\n * whether a `StableId` UUID, a heal-synthesized v5 UUID or other arbitrary string identifier.\n */\nexport function decompressIdentifierIfNeeded(\n\tid: SessionSpaceCompressedId | string,\n\tidCompressor: Pick<IIdCompressor, \"decompress\">,\n): string {\n\tif (typeof id === \"string\") {\n\t\treturn id;\n\t} else {\n\t\tconst decompressed = idCompressor.decompress(id);\n\t\tassert(typeof decompressed === \"string\", 0xd0b /* Decompressed id must be a string */);\n\t\treturn decompressed;\n\t}\n}\n\n/**\n * Options for creating an {@link IdDecodingContext} without an originator.\n * @remarks\n * Decodes {@link OriginatorlessEncodedId} instances, and optionally has a {@link IdentifierHealingConfig} fallback for {@link OriginatorDependentEncodedId} instances.\n */\nexport interface IdDecoderOptionsOriginatorless {\n\treadonly idCompressor: IIdCompressor;\n\t/**\n\t * Optional {@link IdentifierHealingConfig} to use for healing unresolvable identifiers.\n\t * @privateRemarks\n\t * This is a required field which can be undefined to make it explicit when this is not provided.\n\t * This explicitness helps ensure we we don't accidentally drop the healing config as that would break\n\t * an edge case which does not get a lot of testing.\n\t */\n\treadonly healing: IdentifierHealingConfig | undefined;\n}\n\n/**\n * Options for creating an {@link IdDecodingContext} with an originator.\n * @remarks\n * Decodes {@link OriginatorDependentEncodedId} instances.\n */\nexport interface IdDecoderOptionsWithOriginator {\n\treadonly idCompressor: IIdCompressor;\n\t/**\n\t * The ID of the session that encoded the ids.\n\t */\n\treadonly originatorId: SessionId;\n}\n\n/**\n * Context for decoding identifiers.\n * @remarks\n * Depended on how this is constructed, it may or may not handle {@link OriginatorDependentEncodedId} instances.\n *\n * This intentionally avoids exposing anything which depends on the underlying id-compressor's session ID to avoid confusion with the session ID of the compressor which encoded the data.\n * If the session ID of the encoder which encoded the data is known, that information is baked into `resolveEncodedId`.\n *\n * @privateRemarks\n * We have code (FieldBatchDecodingContext) which sometimes has a session ID and sometimes doesn't, so we need a flexible decoding context.\n * Additionally, since this is about serialized data, having stronger types is often counterproductive, as it just relies more on type casts when parsing.\n * This is why we use this flexible pattern rather than code strongly typed over which kind of encoded ids are expected.\n */\nexport class IdDecodingContext {\n\t/**\n\t * Used internally to prevent the use of this decoder in incremental chunks if it has a session id (which would be wrong in those chunks).\n\t */\n\tprotected readonly hasOriginatorSessionId: boolean;\n\n\t/**\n\t * Compressor which can decompress session-space identifiers from {@link resolveEncodedId} as needed.\n\t */\n\tpublic readonly idCompressor: Pick<IIdCompressor, \"decompress\">;\n\n\t/**\n\t * Resolves an encoded op-space identifier to either a session-space ID\n\t * (which {@link idCompressor} can decompress if needed)\n\t * or a string (which passes through unchanged).\n\t * @remarks\n\t * In contexts where non-final identifiers can't be supported (where no originator session is available),\n\t * if a non-final identifier is encountered, this may throw or perform a data healing workaround.\n\t * See {@link FieldBatchDecodingContext.forOp} and {@link FieldBatchDecodingContext.forSummary} for details.\n\t */\n\tpublic readonly resolveEncodedId: (\n\t\tid: OpSpaceCompressedId,\n\t) => SessionSpaceCompressedId | string;\n\n\t/**\n\t * Creates a new instance of the context.\n\t * @param options - The options for creating the context.\n\t */\n\tpublic constructor(\n\t\toptions: IdDecoderOptionsOriginatorless | IdDecoderOptionsWithOriginator,\n\t) {\n\t\tthis.idCompressor = options.idCompressor;\n\t\tif (\"originatorId\" in options) {\n\t\t\tthis.hasOriginatorSessionId = true;\n\t\t\tthis.resolveEncodedId = (id): SessionSpaceCompressedId | string =>\n\t\t\t\toptions.idCompressor.normalizeToSessionSpace(id, options.originatorId);\n\t\t} else {\n\t\t\tthis.hasOriginatorSessionId = false;\n\t\t\tthis.resolveEncodedId = (id): SessionSpaceCompressedId | string =>\n\t\t\t\tforceDecodeEncodedIdWithoutSession(id, options.idCompressor, options.healing);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"compressedIds.js","sourceRoot":"","sources":["../../src/util/compressedIds.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { LogLevel, type ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type {\n\tIIdCompressor,\n\tOpSpaceCompressedId,\n\tSessionId,\n\tSessionSpaceCompressedId,\n} from \"@fluidframework/id-compressor\";\nimport { isFinalId, isStableId } from \"@fluidframework/id-compressor/internal\";\nimport { v5 as uuidV5 } from \"uuid\";\n\n/**\n * An encoded identifier that can be decoded without an originator session id.\n * A finalized compressed id — the same numeric value in op-space and session-space.\n * @remarks\n * These are safe to use in contexts where the encoding id compressor is available to decode them,\n * even when the session id of the encoding id compressor is not known.\n */\nexport type OriginatorlessEncodedId = SessionSpaceCompressedId & OpSpaceCompressedId;\n\n/**\n * An encoded identifier that may require an originator session id to decode.\n * Any op-space compressed id, either finalized or session-local.\n * @remarks\n * These are only safe to use in contexts where the encoding id compressor is available\n * and the session id of the encoding id compressor is known.\n * Currently the only such case is in ops, excluding attach summary ops.\n */\nexport type OriginatorDependentEncodedId = OpSpaceCompressedId;\n\n/**\n * Selects how identifiers are encoded for transport/persistence.\n */\nexport enum EncodedIdType {\n\t/**\n\t * {@link OriginatorlessEncodedId}.\n\t */\n\tOriginatorless,\n\t/**\n\t * {@link OriginatorDependentEncodedId}.\n\t */\n\tOriginatorDependent,\n}\n\nexport type EncodedId<T extends EncodedIdType> = T extends EncodedIdType.Originatorless\n\t? OriginatorlessEncodedId\n\t: T extends EncodedIdType.OriginatorDependent\n\t\t? OriginatorDependentEncodedId\n\t\t: OriginatorlessEncodedId | OriginatorDependentEncodedId;\n\n/**\n * Context for encoding identifiers.\n * @remarks\n * See {@link FieldBatchDecodingContext} for the decoder.\n */\nexport interface IdEncodingContext {\n\tencodePossiblyCompressedId(id: string): string | EncodedId<EncodedIdType>;\n}\n\n/**\n * Encode a string identifier into a possibly compressed form based on the requested mode.\n *\n * @param id - The string identifier to encode.\n * @param idCompressor - The ID compressor to use for compression.\n * @param encodedIdType - The type of encoding to use. If the context doing the decoding will have the originator {@link SessionId},\n * then {@link EncodedIdType.OriginatorDependent} can be used.\n * Otherwise, {@link EncodedIdType.Originatorless} must be used.\n *\n * @privateRemarks\n * Performance:\n * When trying to optimize to avoid unnecessary identifier decompression, and store identifiers compressed in memory,\n * this will likely need to be updated to allow in SessionSpaceCompressedIds.\n */\nexport function encodePossiblyCompressedId<T extends EncodedIdType>(\n\tid: string,\n\tidCompressor: IIdCompressor,\n\tencodedIdType: T,\n): string | EncodedId<T> {\n\tif (!isStableId(id)) {\n\t\treturn id;\n\t}\n\tconst sessionSpaceCompressedId = idCompressor.tryRecompress(id);\n\tif (sessionSpaceCompressedId === undefined) {\n\t\treturn id;\n\t}\n\tconst opSpaceId = idCompressor.normalizeToOpSpace(sessionSpaceCompressedId);\n\tif (encodedIdType === EncodedIdType.Originatorless) {\n\t\treturn isFinalId(opSpaceId) ? (opSpaceId as unknown as EncodedId<T>) : id;\n\t}\n\treturn opSpaceId as unknown as EncodedId<T>;\n}\n\n/**\n * Namespace used for the deterministic UUIDv5 produced by the heal-on-decode workaround\n * in {@link forceDecodeEncodedIdWithoutSession}.\n *\n * This scheme requires consensus across all clients to function — every client that\n * encounters the same unresolvable id must produce the same v5 UUID.\n */\nconst healingNamespace = \"f8a89df3-6882-400f-b913-4c1f6f0157bd\";\n\n/**\n * Decode an {@link OriginatorlessEncodedId} without needing an originator session id.\n *\n * The id is guaranteed to be final by the type contract; it is normalized via\n * {@link IIdCompressor.tryNormalizeToSessionSpaceWithoutSession}, which handles the\n * cluster-aligned-local case rather than relying on the bare numeric value.\n */\nexport function decodeOriginatorlessEncodedId(\n\tid: OriginatorlessEncodedId,\n\tidCompressor: IIdCompressor,\n): SessionSpaceCompressedId {\n\tconst sessionSpaceId = idCompressor.tryNormalizeToSessionSpaceWithoutSession(id);\n\tassert(\n\t\tsessionSpaceId !== undefined,\n\t\t0xd0a /* OriginatorlessEncodedId must be a finalized compressed id at runtime */,\n\t);\n\treturn sessionSpaceId;\n}\n\n/**\n * Decode an {@link OriginatorDependentEncodedId} using a known originator session id.\n */\nexport function decodeEncodedIdWithOriginator(\n\tid: OriginatorDependentEncodedId,\n\toriginator: SessionId,\n\tidCompressor: IIdCompressor,\n): SessionSpaceCompressedId {\n\treturn idCompressor.normalizeToSessionSpace(id, originator);\n}\n\n/**\n * Decode an {@link OriginatorDependentEncodedId} without a session.\n *\n * Returns the session-space id if `id` is finalized (and therefore resolvable\n * without an originator session), or `undefined` if `id` is a non-final op-space\n * id that cannot be resolved without the originator session.\n */\nexport function tryDecodeEncodedIdWithoutSession(\n\tid: OriginatorDependentEncodedId,\n\tidCompressor: IIdCompressor,\n): SessionSpaceCompressedId | undefined {\n\treturn idCompressor.tryNormalizeToSessionSpaceWithoutSession(id);\n}\n\n/**\n * Configuration for the heal-on-decode workaround. The internal counterpart of\n * the user-facing {@link SharedTreeOptionsBeta.healUnresolvableIdentifiersOnDecode}\n * option — see that option for the user-facing rationale and trade-offs.\n *\n * Carried by decode-side contexts ({@link IdDecoderOptionsOriginatorless},\n * `ChangeEncodingContext.healing`,\n * `EditManagerEncodingContext.healing`, etc.) when the workaround is enabled.\n * Presence enables healing; `undefined` means healing is disabled. There is no separate boolean,\n * which makes it impossible to enable healing without supplying the namespace\n * input.\n */\nexport interface IdentifierHealingConfig {\n\t/**\n\t * The SharedTree's shared-object id, used as the v5 namespace input so\n\t * healed UUIDs are unique across attaches of different documents with the\n\t * same session offsets.\n\t */\n\treadonly sharedObjectId: string;\n\n\t/**\n\t * Optional logger used by {@link forceDecodeEncodedIdWithoutSession} to record telemetry\n\t * when the heal-on-decode recovery path is taken (a non-final identifier is healed).\n\t */\n\treadonly logger?: ITelemetryBaseLogger;\n}\n\n/**\n * Decode an op-space compressed id without a session.\n *\n * Finalized ids are returned as session-space ids (final ids are invariant\n * across the two spaces). Non-final ids are healed via {@link IdentifierHealingConfig}\n * if provided, or cause the resolver to throw otherwise. A healed result is a\n * deterministic v5 UUID string — *not* a `StableId`, since that brand requires\n * v4, but still a valid identifier value; the `string` arm of the return type\n * covers this case.\n *\n * The recovery (heal) path only occurs because of a prior bug where non-finalized identifiers were\n * written into summaries. When {@link IdentifierHealingConfig.logger} is supplied, a telemetry event\n * is recorded on that path so heals can be observed in the wild. The error/throw path is intentionally\n * not instrumented here: the thrown exception is expected to surface via the application's own error\n * telemetry, and should never be silently swallowed.\n *\n * @param id - The op-space compressed ID to decode.\n * @param idCompressor - The ID compressor used to normalize the ID.\n * @param healing - Heal-on-decode configuration. Presence enables healing of non-final IDs.\n */\nexport function forceDecodeEncodedIdWithoutSession(\n\tid: OpSpaceCompressedId,\n\tidCompressor: IIdCompressor,\n\thealing: IdentifierHealingConfig | undefined,\n): SessionSpaceCompressedId | string {\n\tconst decoded = tryDecodeEncodedIdWithoutSession(id, idCompressor);\n\tif (decoded !== undefined) {\n\t\treturn decoded;\n\t}\n\t// `id` is a non-final op-space compressed id.\n\tif (healing !== undefined) {\n\t\tconst healed = uuidV5(`${healing.sharedObjectId}|${id}`, healingNamespace);\n\t\thealing.logger?.send(\n\t\t\t{\n\t\t\t\tcategory: \"generic\",\n\t\t\t\teventName: \"HealUnresolvableIdentifierOnDecode\",\n\t\t\t},\n\t\t\t// This telemetry should be very low-volume (and possibly never happen at all),\n\t\t\t// as should only occur when loading documents that were corrupted by a now fixed bug and have not been re-summarized since healing was added.\n\t\t\t// It is useful for monitoring and diagnosing for this to be reported in the same cases in which the error case would be reported,\n\t\t\t// which is the essential level.\n\t\t\tLogLevel.essential,\n\t\t);\n\t\treturn healed;\n\t}\n\tthrow new Error(\n\t\t\"Summary could not be loaded due to an incorrectly encoded identifier. See SharedTreeOptionsBeta.healUnresolvableIdentifiersOnDecode for mitigation.\",\n\t);\n}\n\n/**\n * Convert a decoded, but possibly compressed, identifier to the decompressed string format.\n *\n * Session-space compressed ids are decompressed to their UUID representation via the compressor.\n * String inputs pass through unchanged — they are already in the stored form,\n * whether a `StableId` UUID, a heal-synthesized v5 UUID or other arbitrary string identifier.\n */\nexport function decompressIdentifierIfNeeded(\n\tid: SessionSpaceCompressedId | string,\n\tidCompressor: Pick<IIdCompressor, \"decompress\">,\n): string {\n\tif (typeof id === \"string\") {\n\t\treturn id;\n\t} else {\n\t\tconst decompressed = idCompressor.decompress(id);\n\t\tassert(typeof decompressed === \"string\", 0xd0b /* Decompressed id must be a string */);\n\t\treturn decompressed;\n\t}\n}\n\n/**\n * Options for creating an {@link IdDecodingContext} without an originator.\n * @remarks\n * Decodes {@link OriginatorlessEncodedId} instances, and optionally has a {@link IdentifierHealingConfig} fallback for {@link OriginatorDependentEncodedId} instances.\n */\nexport interface IdDecoderOptionsOriginatorless {\n\treadonly idCompressor: IIdCompressor;\n\t/**\n\t * Optional {@link IdentifierHealingConfig} to use for healing unresolvable identifiers.\n\t * @privateRemarks\n\t * This is a required field which can be undefined to make it explicit when this is not provided.\n\t * This explicitness helps ensure we we don't accidentally drop the healing config as that would break\n\t * an edge case which does not get a lot of testing.\n\t */\n\treadonly healing: IdentifierHealingConfig | undefined;\n}\n\n/**\n * Options for creating an {@link IdDecodingContext} with an originator.\n * @remarks\n * Decodes {@link OriginatorDependentEncodedId} instances.\n */\nexport interface IdDecoderOptionsWithOriginator {\n\treadonly idCompressor: IIdCompressor;\n\t/**\n\t * The ID of the session that encoded the ids.\n\t */\n\treadonly originatorId: SessionId;\n}\n\n/**\n * Context for decoding identifiers.\n * @remarks\n * Depended on how this is constructed, it may or may not handle {@link OriginatorDependentEncodedId} instances.\n *\n * This intentionally avoids exposing anything which depends on the underlying id-compressor's session ID to avoid confusion with the session ID of the compressor which encoded the data.\n * If the session ID of the encoder which encoded the data is known, that information is baked into `resolveEncodedId`.\n *\n * @privateRemarks\n * We have code (FieldBatchDecodingContext) which sometimes has a session ID and sometimes doesn't, so we need a flexible decoding context.\n * Additionally, since this is about serialized data, having stronger types is often counterproductive, as it just relies more on type casts when parsing.\n * This is why we use this flexible pattern rather than code strongly typed over which kind of encoded ids are expected.\n */\nexport class IdDecodingContext {\n\t/**\n\t * Whether this context resolves identifiers using an originator session ID.\n\t * @remarks\n\t * Consulted by {@link FieldBatchDecodingContext} to prevent using an originator-based\n\t * decoder in incremental chunks (which may come from other sessions, making such a\n\t * decoder wrong there).\n\t */\n\tpublic readonly hasOriginatorSessionId: boolean;\n\n\t/**\n\t * Compressor which can decompress session-space identifiers from {@link resolveEncodedId} as needed.\n\t */\n\tpublic readonly idCompressor: Pick<IIdCompressor, \"decompress\">;\n\n\t/**\n\t * Resolves an encoded op-space identifier to either a session-space ID\n\t * (which {@link idCompressor} can decompress if needed)\n\t * or a string (which passes through unchanged).\n\t * @remarks\n\t * In contexts where non-final identifiers can't be supported (where no originator session is available),\n\t * if a non-final identifier is encountered, this may throw or perform a data healing workaround.\n\t * See {@link FieldBatchDecodingContext.forOp} and {@link FieldBatchDecodingContext.forSummary} for details.\n\t */\n\tpublic readonly resolveEncodedId: (\n\t\tid: OpSpaceCompressedId,\n\t) => SessionSpaceCompressedId | string;\n\n\t/**\n\t * Creates a new instance of the context.\n\t * @param options - The options for creating the context.\n\t */\n\tpublic constructor(\n\t\toptions: IdDecoderOptionsOriginatorless | IdDecoderOptionsWithOriginator,\n\t) {\n\t\tthis.idCompressor = options.idCompressor;\n\t\tif (\"originatorId\" in options) {\n\t\t\tthis.hasOriginatorSessionId = true;\n\t\t\tthis.resolveEncodedId = (id): SessionSpaceCompressedId | string =>\n\t\t\t\toptions.idCompressor.normalizeToSessionSpace(id, options.originatorId);\n\t\t} else {\n\t\t\tthis.hasOriginatorSessionId = false;\n\t\t\tthis.resolveEncodedId = (id): SessionSpaceCompressedId | string =>\n\t\t\t\tforceDecodeEncodedIdWithoutSession(id, options.idCompressor, options.healing);\n\t\t}\n\t}\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/tree",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.116.0-416006",
|
|
4
4
|
"description": "Distributed tree",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -101,17 +101,17 @@
|
|
|
101
101
|
"temp-directory": "nyc/.nyc_output"
|
|
102
102
|
},
|
|
103
103
|
"dependencies": {
|
|
104
|
-
"@fluid-internal/client-utils": "
|
|
105
|
-
"@fluidframework/core-interfaces": "
|
|
106
|
-
"@fluidframework/core-utils": "
|
|
107
|
-
"@fluidframework/datastore-definitions": "
|
|
108
|
-
"@fluidframework/driver-definitions": "
|
|
109
|
-
"@fluidframework/id-compressor": "
|
|
110
|
-
"@fluidframework/runtime-definitions": "
|
|
111
|
-
"@fluidframework/runtime-utils": "
|
|
112
|
-
"@fluidframework/shared-object-base": "
|
|
113
|
-
"@fluidframework/telemetry-utils": "
|
|
114
|
-
"@fluidframework/type-factory": "
|
|
104
|
+
"@fluid-internal/client-utils": "2.116.0-416006",
|
|
105
|
+
"@fluidframework/core-interfaces": "2.116.0-416006",
|
|
106
|
+
"@fluidframework/core-utils": "2.116.0-416006",
|
|
107
|
+
"@fluidframework/datastore-definitions": "2.116.0-416006",
|
|
108
|
+
"@fluidframework/driver-definitions": "2.116.0-416006",
|
|
109
|
+
"@fluidframework/id-compressor": "2.116.0-416006",
|
|
110
|
+
"@fluidframework/runtime-definitions": "2.116.0-416006",
|
|
111
|
+
"@fluidframework/runtime-utils": "2.116.0-416006",
|
|
112
|
+
"@fluidframework/shared-object-base": "2.116.0-416006",
|
|
113
|
+
"@fluidframework/telemetry-utils": "2.116.0-416006",
|
|
114
|
+
"@fluidframework/type-factory": "2.116.0-416006",
|
|
115
115
|
"@sinclair/typebox": "^0.34.13",
|
|
116
116
|
"@tylerbu/sorted-btree-es6": "^2.1.1",
|
|
117
117
|
"semver-ts": "^1.0.3",
|
|
@@ -121,21 +121,21 @@
|
|
|
121
121
|
"devDependencies": {
|
|
122
122
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
123
123
|
"@biomejs/biome": "~2.4.5",
|
|
124
|
-
"@fluid-internal/mocha-test-setup": "
|
|
125
|
-
"@fluid-private/stochastic-test-utils": "
|
|
126
|
-
"@fluid-private/test-dds-utils": "
|
|
127
|
-
"@fluid-private/test-drivers": "
|
|
124
|
+
"@fluid-internal/mocha-test-setup": "2.116.0-416006",
|
|
125
|
+
"@fluid-private/stochastic-test-utils": "2.116.0-416006",
|
|
126
|
+
"@fluid-private/test-dds-utils": "2.116.0-416006",
|
|
127
|
+
"@fluid-private/test-drivers": "2.116.0-416006",
|
|
128
128
|
"@fluid-tools/benchmark": "^0.59.0",
|
|
129
129
|
"@fluid-tools/build-cli": "^0.65.0",
|
|
130
130
|
"@fluidframework/build-common": "^2.0.3",
|
|
131
131
|
"@fluidframework/build-tools": "^0.65.0",
|
|
132
|
-
"@fluidframework/container-definitions": "
|
|
133
|
-
"@fluidframework/container-loader": "
|
|
134
|
-
"@fluidframework/container-runtime": "
|
|
132
|
+
"@fluidframework/container-definitions": "2.116.0-416006",
|
|
133
|
+
"@fluidframework/container-loader": "2.116.0-416006",
|
|
134
|
+
"@fluidframework/container-runtime": "2.116.0-416006",
|
|
135
135
|
"@fluidframework/eslint-config-fluid": "^13.0.0",
|
|
136
|
-
"@fluidframework/local-driver": "
|
|
137
|
-
"@fluidframework/test-runtime-utils": "
|
|
138
|
-
"@fluidframework/test-utils": "
|
|
136
|
+
"@fluidframework/local-driver": "2.116.0-416006",
|
|
137
|
+
"@fluidframework/test-runtime-utils": "2.116.0-416006",
|
|
138
|
+
"@fluidframework/test-utils": "2.116.0-416006",
|
|
139
139
|
"@fluidframework/tree-previous": "npm:@fluidframework/tree@2.112.0",
|
|
140
140
|
"@microsoft/api-extractor": "7.58.1",
|
|
141
141
|
"@types/diff": "^3.5.1",
|
|
@@ -159,6 +159,9 @@
|
|
|
159
159
|
"rimraf": "^6.1.3",
|
|
160
160
|
"typescript": "~5.4.5"
|
|
161
161
|
},
|
|
162
|
+
"comments": [
|
|
163
|
+
"Tree test builds and eslint depend on the corresponding ID Compressor test builds because Tree tests import its internal/test-utils export, which resolves to generated test output."
|
|
164
|
+
],
|
|
162
165
|
"fluidBuild": {
|
|
163
166
|
"tasks": {
|
|
164
167
|
"build:test:cjs": [
|
|
@@ -171,6 +174,10 @@
|
|
|
171
174
|
],
|
|
172
175
|
"ci:build:docs": [
|
|
173
176
|
"build:esnext"
|
|
177
|
+
],
|
|
178
|
+
"eslint": [
|
|
179
|
+
"...",
|
|
180
|
+
"@fluidframework/id-compressor#build:test:esm"
|
|
174
181
|
]
|
|
175
182
|
}
|
|
176
183
|
},
|
package/src/core/index.ts
CHANGED
package/src/core/rebase/index.ts
CHANGED
package/src/core/rebase/types.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type { Listenable } from "@fluidframework/core-interfaces";
|
|
6
7
|
import type {
|
|
7
8
|
OpSpaceCompressedId,
|
|
8
9
|
SessionId,
|
|
@@ -271,6 +272,72 @@ export interface LocalChangeMetadata extends CommitMetadata {
|
|
|
271
272
|
* ```
|
|
272
273
|
*/
|
|
273
274
|
readonly labels: TransactionLabels;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Events related to a local change that has been applied.
|
|
278
|
+
*/
|
|
279
|
+
readonly events: Listenable<LocalCommitEvents>;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Events related to a local commit that has been applied.
|
|
284
|
+
* @sealed @alpha
|
|
285
|
+
*/
|
|
286
|
+
export interface LocalCommitEvents {
|
|
287
|
+
/**
|
|
288
|
+
* Fired once a commit has been ordered by the sequencing service.
|
|
289
|
+
* @param outcome - information about what changes from the commit were applied or not
|
|
290
|
+
*
|
|
291
|
+
* @remarks
|
|
292
|
+
* Once a commit is sequenced, the following guarantees hold:
|
|
293
|
+
* 1. The changes carried by the commit have been persisted and other peers are able to see them.
|
|
294
|
+
* 2. There can be no more concurrent changes sequenced before this commit, which means this commit has reached its settled form.
|
|
295
|
+
*
|
|
296
|
+
* This event can be used by applications to inform the end user that their changes have been saved (`CommitOutcome.FullyApplied`) or rejected (`CommitOutcome.FullyDropped` and `CommitOutcome.NewContentOnly`).
|
|
297
|
+
* It can also be used to queue up a new attempt at making the rejected changes. Note however that new edits must be made outside of the event callback.
|
|
298
|
+
* @example Notifying the user of the outcome and allowing them to retry:
|
|
299
|
+
* ```typescript
|
|
300
|
+
* // Use `asAlpha` API to access the settled event API
|
|
301
|
+
* const view = asAlpha(tree.viewWith(config));
|
|
302
|
+
*
|
|
303
|
+
* // Function to clear all contents of the tree, with a precondition that no changes have occurred.
|
|
304
|
+
* const clearAllContents = () => {
|
|
305
|
+
* view.runTransaction(
|
|
306
|
+
* () => {
|
|
307
|
+
* // Remove all contents at the root
|
|
308
|
+
* view.root.removeRange();
|
|
309
|
+
* },
|
|
310
|
+
* { preconditions: [{ type: "noChange" }] },
|
|
311
|
+
* );
|
|
312
|
+
* };
|
|
313
|
+
*
|
|
314
|
+
* // Register the logic for notifying the user of the outcome and allow them to retry
|
|
315
|
+
* view.events.on("changed", (metadata) => {
|
|
316
|
+
* if (metadata.isLocal) {
|
|
317
|
+
* metadata.events.on("settled", (outcome) => {
|
|
318
|
+
* if (outcome === CommitOutcome.FullyApplied) {
|
|
319
|
+
* alert("Clear operation succeeded.");
|
|
320
|
+
* } else {
|
|
321
|
+
* const shouldTryAgain = confirm(
|
|
322
|
+
* "The contents have changed. Do you still want to clear everything?",
|
|
323
|
+
* );
|
|
324
|
+
* if (shouldTryAgain) {
|
|
325
|
+
* // It is invalid to make edits during the event callback, so we schedule the retry to occur asynchronously.
|
|
326
|
+
* setTimeout(clearAllContents);
|
|
327
|
+
* } else {
|
|
328
|
+
* alert("Clear operation aborted.");
|
|
329
|
+
* }
|
|
330
|
+
* }
|
|
331
|
+
* });
|
|
332
|
+
* }
|
|
333
|
+
* });
|
|
334
|
+
*
|
|
335
|
+
* // First attempt to clear all contents.
|
|
336
|
+
* // This will synchronously trigger the changed "event" and register the listener for the settled event.
|
|
337
|
+
* clearAllContents();
|
|
338
|
+
* ```
|
|
339
|
+
*/
|
|
340
|
+
settled(outcome: CommitOutcome): void;
|
|
274
341
|
}
|
|
275
342
|
|
|
276
343
|
/**
|
|
@@ -311,6 +378,52 @@ export interface LabelTree {
|
|
|
311
378
|
*/
|
|
312
379
|
export type TransactionLabels = Set<unknown> & { tree?: LabelTree };
|
|
313
380
|
|
|
381
|
+
/**
|
|
382
|
+
* Details about what changes from a commit were applied or not.
|
|
383
|
+
* @alpha
|
|
384
|
+
*/
|
|
385
|
+
export enum CommitOutcome {
|
|
386
|
+
/**
|
|
387
|
+
* All of the changes in the commit were applied.
|
|
388
|
+
*/
|
|
389
|
+
FullyApplied,
|
|
390
|
+
/**
|
|
391
|
+
* None of the changes in the commit were applied.
|
|
392
|
+
* @remarks
|
|
393
|
+
* This occurs when an implicit constraint has been violated.
|
|
394
|
+
* Implicit constraints are those that are automatically enforced by SharedTree on all changes.
|
|
395
|
+
*
|
|
396
|
+
* Such a violation typically arises in one of two scenarios:
|
|
397
|
+
* 1. A schema change conflicts with a concurrent data or schema change that was sequenced before it.
|
|
398
|
+
* 2. A data change conflicts with a concurrent schema change that was sequenced before it.
|
|
399
|
+
*/
|
|
400
|
+
FullyDropped,
|
|
401
|
+
/**
|
|
402
|
+
* Only the creation of new content was applied.
|
|
403
|
+
* All other changes (including the insertion and/or modification of the new content) were dropped.
|
|
404
|
+
* @remarks
|
|
405
|
+
* This occurs when at least one explicit constraint has been violated
|
|
406
|
+
* (and no implicit constraints were violated.)
|
|
407
|
+
*
|
|
408
|
+
* Explicit constraints are those that are explicitly added
|
|
409
|
+
* through {@link RunTransactionParamsAlpha.preconditions | preconditions}
|
|
410
|
+
* or {@link TransactionCallbackStatusAlpha.preconditionsOnRevert | preconditionsOnRevert}.
|
|
411
|
+
*
|
|
412
|
+
* The new content may be edited (and potentially inserted) by subsequent commits,
|
|
413
|
+
* assuming those commits are not themselves subject to constraint violations.
|
|
414
|
+
* Note that, if left uninserted, new content will eventually be garbage-collected from the document.
|
|
415
|
+
*
|
|
416
|
+
* Applications typically choose to treat this outcome as equivalent to {@link CommitOutcome.FullyDropped | FullyDropped}
|
|
417
|
+
* and, when reattempting the change, generate a different copy of the new content if any.
|
|
418
|
+
*
|
|
419
|
+
* @privateRemarks
|
|
420
|
+
* New content is preserved so that subsequent commits that reference it without having to carry their own copies of it.
|
|
421
|
+
* This can become expensive: one extra copy per subsequent commit, included in both in cases where we know the prior commit was dropped and in cases where we don't yet know.
|
|
422
|
+
* We could instead drop all subsequent commits that reference the new content, but that would create a greater risk of data loss.
|
|
423
|
+
*/
|
|
424
|
+
NewContentOnly,
|
|
425
|
+
}
|
|
426
|
+
|
|
314
427
|
/**
|
|
315
428
|
* Information about a change that has been applied by a remote client.
|
|
316
429
|
* @sealed @alpha
|
package/src/entrypoints/alpha.ts
CHANGED
|
@@ -178,6 +178,7 @@ export {
|
|
|
178
178
|
ChangeMetadata,
|
|
179
179
|
CodecName,
|
|
180
180
|
CodecWriteOptions,
|
|
181
|
+
CommitOutcome,
|
|
181
182
|
Component,
|
|
182
183
|
CreateIndependentTreeAlphaOptions,
|
|
183
184
|
DirtyTreeMap,
|
|
@@ -197,6 +198,7 @@ export {
|
|
|
197
198
|
FormatValidatorBasic,
|
|
198
199
|
FormatValidatorNoOp,
|
|
199
200
|
FormatVersion,
|
|
201
|
+
FormattedText,
|
|
200
202
|
HandleConverter,
|
|
201
203
|
ICodecOptions,
|
|
202
204
|
ITreeAlpha,
|
|
@@ -229,6 +231,7 @@ export {
|
|
|
229
231
|
JsonTreeSchema,
|
|
230
232
|
LabelTree,
|
|
231
233
|
LocalChangeMetadata,
|
|
234
|
+
LocalCommitEvents,
|
|
232
235
|
MapNodeCustomizableSchema,
|
|
233
236
|
MapNodeCustomizableSchemaUnsafe,
|
|
234
237
|
MapNodePojoEmulationSchema,
|
|
@@ -244,6 +247,7 @@ export {
|
|
|
244
247
|
ObjectNodeSchemaWorkaround,
|
|
245
248
|
ObjectSchemaOptionsAlpha,
|
|
246
249
|
ObservationResults,
|
|
250
|
+
PlainText,
|
|
247
251
|
ReadSchema,
|
|
248
252
|
ReadableField,
|
|
249
253
|
RecordNodeCustomizableSchema,
|
|
@@ -256,9 +260,9 @@ export {
|
|
|
256
260
|
SchemaFactoryAlpha,
|
|
257
261
|
SchemaStaticsAlpha,
|
|
258
262
|
SchemaType,
|
|
259
|
-
SharedTreeAlpha,
|
|
260
|
-
SharedTreeFormatOptions,
|
|
261
|
-
SharedTreeOptions,
|
|
263
|
+
SharedTreeAlpha,
|
|
264
|
+
SharedTreeFormatOptions,
|
|
265
|
+
SharedTreeOptions,
|
|
262
266
|
SimpleAllowedTypeAttributes,
|
|
263
267
|
SimpleArrayNodeSchema,
|
|
264
268
|
SimpleFieldSchema,
|
|
@@ -272,7 +276,7 @@ export {
|
|
|
272
276
|
SimpleTreeSchema,
|
|
273
277
|
StagedSchemaUpgradePolicy,
|
|
274
278
|
StagedSchemaUpgradePolicyFactory,
|
|
275
|
-
|
|
279
|
+
StringSchema,
|
|
276
280
|
TransactionCallbackStatusAlpha,
|
|
277
281
|
TransactionConstraintAlpha,
|
|
278
282
|
TransactionLabels,
|
|
@@ -309,6 +313,7 @@ export {
|
|
|
309
313
|
createArrayInsertionAnchor,
|
|
310
314
|
createIndependentTreeAlpha,
|
|
311
315
|
decodeSchemaCompatibilitySnapshot,
|
|
316
|
+
defineTreeDataStore,
|
|
312
317
|
encodeSchemaCompatibilitySnapshot,
|
|
313
318
|
eraseSchemaDetails,
|
|
314
319
|
eraseSchemaDetailsSubclassable,
|
|
@@ -330,7 +335,6 @@ export {
|
|
|
330
335
|
replaceConciseTreeHandles,
|
|
331
336
|
replaceHandles,
|
|
332
337
|
replaceVerboseTreeHandles,
|
|
333
|
-
trackDirtyNodes
|
|
334
|
-
defineTreeDataStore
|
|
338
|
+
trackDirtyNodes
|
|
335
339
|
// #endregion
|
|
336
340
|
} from "../index.js";
|
|
@@ -133,10 +133,10 @@ export interface FieldBatchEncodingContext {
|
|
|
133
133
|
/**
|
|
134
134
|
* Decode-side context for {@link FieldBatchCodec}.
|
|
135
135
|
*
|
|
136
|
-
*
|
|
137
|
-
* originator-session lookup and
|
|
138
|
-
* the deterministic UUIDv5 synthesis
|
|
139
|
-
*
|
|
136
|
+
* Composes an {@link IdDecodingContext} (which carries the per-call `resolveEncodedId`
|
|
137
|
+
* function encapsulating the originator-session lookup and, for the forest-summarizer's
|
|
138
|
+
* legacy heal path, the deterministic UUIDv5 synthesis) together with the optional
|
|
139
|
+
* incremental decoder for this batch.
|
|
140
140
|
*
|
|
141
141
|
* Constructed via one of the two named static factories — {@link forOp} or
|
|
142
142
|
* {@link forSummary} — depending on the call site's semantics. The constructor
|
|
@@ -144,18 +144,19 @@ export interface FieldBatchEncodingContext {
|
|
|
144
144
|
* op-style and summary-style decoding is load-bearing (different invariants
|
|
145
145
|
* apply, and bugs in this area are typically the result of conflating them).
|
|
146
146
|
*/
|
|
147
|
-
export class FieldBatchDecodingContext
|
|
147
|
+
export class FieldBatchDecodingContext {
|
|
148
148
|
private constructor(
|
|
149
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Resolves the encoded identifiers contained in this batch.
|
|
151
|
+
*/
|
|
152
|
+
public readonly idDecodingContext: IdDecodingContext,
|
|
150
153
|
/**
|
|
151
154
|
* Decoder for incremental fields. Defined when the encoded batch contains
|
|
152
155
|
* incremental chunks. Only populated on summary-style contexts; op-style
|
|
153
156
|
* contexts always have this undefined.
|
|
154
157
|
*/
|
|
155
158
|
public readonly incrementalDecoder?: IncrementalDecoder,
|
|
156
|
-
) {
|
|
157
|
-
super(options);
|
|
158
|
-
}
|
|
159
|
+
) {}
|
|
159
160
|
|
|
160
161
|
/**
|
|
161
162
|
* Construct a decode context for an op.
|
|
@@ -167,7 +168,7 @@ export class FieldBatchDecodingContext extends IdDecodingContext {
|
|
|
167
168
|
* a UUID. Incremental decoding is not used for ops.
|
|
168
169
|
*/
|
|
169
170
|
public static forOp(options: IdDecoderOptionsWithOriginator): FieldBatchDecodingContext {
|
|
170
|
-
return new FieldBatchDecodingContext(options);
|
|
171
|
+
return new FieldBatchDecodingContext(new IdDecodingContext(options));
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
/**
|
|
@@ -189,7 +190,7 @@ export class FieldBatchDecodingContext extends IdDecodingContext {
|
|
|
189
190
|
public static forSummary(
|
|
190
191
|
options: IdDecoderOptionsOriginatorless | IdDecoderOptionsWithOriginator,
|
|
191
192
|
): FieldBatchDecodingContext {
|
|
192
|
-
return new FieldBatchDecodingContext(options);
|
|
193
|
+
return new FieldBatchDecodingContext(new IdDecodingContext(options));
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
/**
|
|
@@ -206,10 +207,10 @@ export class FieldBatchDecodingContext extends IdDecodingContext {
|
|
|
206
207
|
// This mitigates the risk of using incorrect originator session ID identifiers in incremental chunks.
|
|
207
208
|
// See also private remarks on forSummary.
|
|
208
209
|
assert(
|
|
209
|
-
!this.hasOriginatorSessionId,
|
|
210
|
+
!this.idDecodingContext.hasOriginatorSessionId,
|
|
210
211
|
0xd0c /* withIncrementalDecoder can only be called on contexts without an originator session ID */,
|
|
211
212
|
);
|
|
212
|
-
return new FieldBatchDecodingContext(this.
|
|
213
|
+
return new FieldBatchDecodingContext(this.idDecodingContext, incrementalDecoder);
|
|
213
214
|
}
|
|
214
215
|
}
|
|
215
216
|
|
|
@@ -298,7 +299,9 @@ function makeFieldBatchCodecForVersion(
|
|
|
298
299
|
context: FieldBatchDecodingContext,
|
|
299
300
|
): FieldBatch => {
|
|
300
301
|
// TODO: consider checking data is in schema.
|
|
301
|
-
return decode(data, context, context.incrementalDecoder).map((chunk) =>
|
|
302
|
+
return decode(data, context.idDecodingContext, context.incrementalDecoder).map((chunk) =>
|
|
303
|
+
chunk.cursor(),
|
|
304
|
+
);
|
|
302
305
|
},
|
|
303
306
|
schema: encodedFieldBatchType,
|
|
304
307
|
};
|