@fluidframework/tree 2.82.0 → 2.83.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/README.md +33 -5
- package/api-report/tree.alpha.api.md +25 -21
- package/api-report/tree.beta.api.md +14 -2
- package/api-report/tree.legacy.beta.api.md +14 -2
- package/api-report/tree.legacy.public.api.md +1 -1
- package/api-report/tree.public.api.md +1 -1
- package/dist/alpha.d.ts +3 -3
- package/dist/beta.d.ts +1 -0
- package/dist/codec/codec.d.ts +3 -39
- package/dist/codec/codec.d.ts.map +1 -1
- package/dist/codec/codec.js +5 -50
- package/dist/codec/codec.js.map +1 -1
- package/dist/codec/index.d.ts +1 -1
- package/dist/codec/index.d.ts.map +1 -1
- package/dist/codec/index.js +1 -2
- package/dist/codec/index.js.map +1 -1
- package/dist/codec/versioned/codec.d.ts +20 -7
- package/dist/codec/versioned/codec.d.ts.map +1 -1
- package/dist/codec/versioned/codec.js +56 -30
- package/dist/codec/versioned/codec.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecs.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodecs.js +6 -4
- package/dist/core/tree/detachedFieldIndexCodecs.js.map +1 -1
- package/dist/extensibleUnionNode.d.ts +97 -0
- package/dist/extensibleUnionNode.d.ts.map +1 -0
- package/dist/{extensibleSchemaUnion.js → extensibleUnionNode.js} +28 -18
- package/dist/extensibleUnionNode.js.map +1 -0
- package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts +1 -1
- package/dist/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/codec/codecs.js +4 -4
- package/dist/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
- package/dist/feature-libraries/forest-summary/codec.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/codec.js +7 -1
- package/dist/feature-libraries/forest-summary/codec.js.map +1 -1
- package/dist/feature-libraries/forest-summary/formatCommon.d.ts +3 -3
- package/dist/feature-libraries/forest-summary/formatCommon.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/formatCommon.js.map +1 -1
- package/dist/feature-libraries/forest-summary/formatV1.d.ts +2 -3
- package/dist/feature-libraries/forest-summary/formatV1.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/formatV1.js +1 -2
- package/dist/feature-libraries/forest-summary/formatV1.js.map +1 -1
- package/dist/feature-libraries/forest-summary/formatV2.d.ts +2 -3
- package/dist/feature-libraries/forest-summary/formatV2.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/formatV2.js +1 -2
- package/dist/feature-libraries/forest-summary/formatV2.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecV1.d.ts +2 -2
- package/dist/feature-libraries/modular-schema/modularChangeCodecV1.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecV1.js +4 -4
- package/dist/feature-libraries/modular-schema/modularChangeCodecV1.js.map +1 -1
- package/dist/feature-libraries/schema-index/codec.d.ts.map +1 -1
- package/dist/feature-libraries/schema-index/codec.js +6 -4
- package/dist/feature-libraries/schema-index/codec.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/shared-tree/sharedTreeChangeCodecs.js +1 -1
- package/dist/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
- package/dist/shared-tree/tree.d.ts +1 -1
- package/dist/shared-tree/tree.js.map +1 -1
- package/dist/shared-tree/treeAlpha.d.ts +1 -1
- package/dist/shared-tree/treeAlpha.js.map +1 -1
- package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
- package/dist/shared-tree/treeCheckout.js +2 -4
- package/dist/shared-tree/treeCheckout.js.map +1 -1
- package/dist/shared-tree-core/editManagerCodecsCommons.d.ts +3 -3
- package/dist/shared-tree-core/editManagerCodecsCommons.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerCodecsCommons.js +2 -2
- package/dist/shared-tree-core/editManagerCodecsCommons.js.map +1 -1
- package/dist/shared-tree-core/editManagerCodecsV1toV4.d.ts +2 -2
- package/dist/shared-tree-core/editManagerCodecsV1toV4.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerCodecsV1toV4.js +1 -1
- package/dist/shared-tree-core/editManagerCodecsV1toV4.js.map +1 -1
- package/dist/shared-tree-core/editManagerCodecsVSharedBranches.d.ts +2 -2
- package/dist/shared-tree-core/editManagerCodecsVSharedBranches.d.ts.map +1 -1
- package/dist/shared-tree-core/editManagerCodecsVSharedBranches.js +1 -1
- package/dist/shared-tree-core/editManagerCodecsVSharedBranches.js.map +1 -1
- package/dist/shared-tree-core/messageCodecs.d.ts.map +1 -1
- package/dist/shared-tree-core/messageCodecs.js +2 -2
- package/dist/shared-tree-core/messageCodecs.js.map +1 -1
- package/dist/simple-tree/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 -2
- package/dist/simple-tree/api/index.js.map +1 -1
- package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts +148 -29
- package/dist/simple-tree/api/snapshotCompatibilityChecker.d.ts.map +1 -1
- package/dist/simple-tree/api/snapshotCompatibilityChecker.js +180 -99
- package/dist/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
- package/dist/simple-tree/api/tree.d.ts +1 -1
- package/dist/simple-tree/api/tree.js.map +1 -1
- package/dist/simple-tree/api/treeBeta.d.ts +1 -1
- package/dist/simple-tree/api/treeBeta.js.map +1 -1
- package/dist/simple-tree/core/allowedTypes.d.ts +1 -1
- package/dist/simple-tree/core/allowedTypes.js.map +1 -1
- package/dist/simple-tree/core/unhydratedFlexTree.d.ts +1 -0
- package/dist/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
- package/dist/simple-tree/core/unhydratedFlexTree.js +29 -0
- package/dist/simple-tree/core/unhydratedFlexTree.js.map +1 -1
- package/dist/simple-tree/index.d.ts +1 -1
- package/dist/simple-tree/index.d.ts.map +1 -1
- package/dist/simple-tree/index.js +2 -2
- package/dist/simple-tree/index.js.map +1 -1
- package/dist/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
- package/dist/simple-tree/node-kinds/array/arrayNode.js +4 -13
- package/dist/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
- package/dist/simple-tree/unhydratedFlexTreeFromInsertable.d.ts.map +1 -1
- package/dist/simple-tree/unhydratedFlexTreeFromInsertable.js +33 -7
- package/dist/simple-tree/unhydratedFlexTreeFromInsertable.js.map +1 -1
- package/dist/text/textDomainFormatted.d.ts +3 -3
- package/dist/text/textDomainFormatted.d.ts.map +1 -1
- package/dist/text/textDomainFormatted.js +48 -32
- package/dist/text/textDomainFormatted.js.map +1 -1
- package/dist/util/bTreeUtils.d.ts.map +1 -1
- package/dist/util/bTreeUtils.js +6 -6
- package/dist/util/bTreeUtils.js.map +1 -1
- package/dist/util/rangeMap.d.ts.map +1 -1
- package/dist/util/rangeMap.js +5 -6
- package/dist/util/rangeMap.js.map +1 -1
- package/lib/alpha.d.ts +3 -3
- package/lib/beta.d.ts +1 -0
- package/lib/codec/codec.d.ts +3 -39
- package/lib/codec/codec.d.ts.map +1 -1
- package/lib/codec/codec.js +4 -47
- package/lib/codec/codec.js.map +1 -1
- package/lib/codec/index.d.ts +1 -1
- package/lib/codec/index.d.ts.map +1 -1
- package/lib/codec/index.js +1 -1
- package/lib/codec/index.js.map +1 -1
- package/lib/codec/versioned/codec.d.ts +20 -7
- package/lib/codec/versioned/codec.d.ts.map +1 -1
- package/lib/codec/versioned/codec.js +59 -33
- package/lib/codec/versioned/codec.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecs.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodecs.js +6 -4
- package/lib/core/tree/detachedFieldIndexCodecs.js.map +1 -1
- package/lib/extensibleUnionNode.d.ts +97 -0
- package/lib/extensibleUnionNode.d.ts.map +1 -0
- package/lib/{extensibleSchemaUnion.js → extensibleUnionNode.js} +28 -18
- package/lib/extensibleUnionNode.js.map +1 -0
- package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts +1 -1
- package/lib/feature-libraries/chunked-forest/codec/codecs.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/codec/codecs.js +5 -5
- package/lib/feature-libraries/chunked-forest/codec/codecs.js.map +1 -1
- package/lib/feature-libraries/forest-summary/codec.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/codec.js +8 -2
- package/lib/feature-libraries/forest-summary/codec.js.map +1 -1
- package/lib/feature-libraries/forest-summary/formatCommon.d.ts +3 -3
- package/lib/feature-libraries/forest-summary/formatCommon.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/formatCommon.js.map +1 -1
- package/lib/feature-libraries/forest-summary/formatV1.d.ts +2 -3
- package/lib/feature-libraries/forest-summary/formatV1.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/formatV1.js +1 -2
- package/lib/feature-libraries/forest-summary/formatV1.js.map +1 -1
- package/lib/feature-libraries/forest-summary/formatV2.d.ts +2 -3
- package/lib/feature-libraries/forest-summary/formatV2.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/formatV2.js +1 -2
- package/lib/feature-libraries/forest-summary/formatV2.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecV1.d.ts +2 -2
- package/lib/feature-libraries/modular-schema/modularChangeCodecV1.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecV1.js +4 -4
- package/lib/feature-libraries/modular-schema/modularChangeCodecV1.js.map +1 -1
- package/lib/feature-libraries/schema-index/codec.d.ts.map +1 -1
- package/lib/feature-libraries/schema-index/codec.js +6 -4
- package/lib/feature-libraries/schema-index/codec.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/shared-tree/sharedTreeChangeCodecs.js +1 -1
- package/lib/shared-tree/sharedTreeChangeCodecs.js.map +1 -1
- package/lib/shared-tree/tree.d.ts +1 -1
- package/lib/shared-tree/tree.js.map +1 -1
- package/lib/shared-tree/treeAlpha.d.ts +1 -1
- package/lib/shared-tree/treeAlpha.js.map +1 -1
- package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
- package/lib/shared-tree/treeCheckout.js +2 -4
- package/lib/shared-tree/treeCheckout.js.map +1 -1
- package/lib/shared-tree-core/editManagerCodecsCommons.d.ts +3 -3
- package/lib/shared-tree-core/editManagerCodecsCommons.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerCodecsCommons.js +2 -2
- package/lib/shared-tree-core/editManagerCodecsCommons.js.map +1 -1
- package/lib/shared-tree-core/editManagerCodecsV1toV4.d.ts +2 -2
- package/lib/shared-tree-core/editManagerCodecsV1toV4.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerCodecsV1toV4.js +2 -2
- package/lib/shared-tree-core/editManagerCodecsV1toV4.js.map +1 -1
- package/lib/shared-tree-core/editManagerCodecsVSharedBranches.d.ts +2 -2
- package/lib/shared-tree-core/editManagerCodecsVSharedBranches.d.ts.map +1 -1
- package/lib/shared-tree-core/editManagerCodecsVSharedBranches.js +2 -2
- package/lib/shared-tree-core/editManagerCodecsVSharedBranches.js.map +1 -1
- package/lib/shared-tree-core/messageCodecs.d.ts.map +1 -1
- package/lib/shared-tree-core/messageCodecs.js +2 -2
- package/lib/shared-tree-core/messageCodecs.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/snapshotCompatibilityChecker.d.ts +148 -29
- package/lib/simple-tree/api/snapshotCompatibilityChecker.d.ts.map +1 -1
- package/lib/simple-tree/api/snapshotCompatibilityChecker.js +179 -98
- package/lib/simple-tree/api/snapshotCompatibilityChecker.js.map +1 -1
- package/lib/simple-tree/api/tree.d.ts +1 -1
- package/lib/simple-tree/api/tree.js.map +1 -1
- package/lib/simple-tree/api/treeBeta.d.ts +1 -1
- package/lib/simple-tree/api/treeBeta.js.map +1 -1
- package/lib/simple-tree/core/allowedTypes.d.ts +1 -1
- package/lib/simple-tree/core/allowedTypes.js.map +1 -1
- package/lib/simple-tree/core/unhydratedFlexTree.d.ts +1 -0
- package/lib/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
- package/lib/simple-tree/core/unhydratedFlexTree.js +29 -0
- package/lib/simple-tree/core/unhydratedFlexTree.js.map +1 -1
- package/lib/simple-tree/index.d.ts +1 -1
- 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/node-kinds/array/arrayNode.d.ts.map +1 -1
- package/lib/simple-tree/node-kinds/array/arrayNode.js +5 -14
- package/lib/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
- package/lib/simple-tree/unhydratedFlexTreeFromInsertable.d.ts.map +1 -1
- package/lib/simple-tree/unhydratedFlexTreeFromInsertable.js +34 -8
- package/lib/simple-tree/unhydratedFlexTreeFromInsertable.js.map +1 -1
- package/lib/text/textDomainFormatted.d.ts +3 -3
- package/lib/text/textDomainFormatted.d.ts.map +1 -1
- package/lib/text/textDomainFormatted.js +30 -14
- package/lib/text/textDomainFormatted.js.map +1 -1
- package/lib/util/bTreeUtils.d.ts.map +1 -1
- package/lib/util/bTreeUtils.js +6 -6
- package/lib/util/bTreeUtils.js.map +1 -1
- package/lib/util/rangeMap.d.ts.map +1 -1
- package/lib/util/rangeMap.js +5 -6
- package/lib/util/rangeMap.js.map +1 -1
- package/package.json +23 -23
- package/src/codec/codec.ts +10 -112
- package/src/codec/index.ts +0 -3
- package/src/codec/versioned/codec.ts +119 -83
- package/src/core/tree/detachedFieldIndexCodecs.ts +6 -4
- package/src/{extensibleSchemaUnion.ts → extensibleUnionNode.ts} +61 -19
- package/src/feature-libraries/chunked-forest/codec/codecs.ts +5 -11
- package/src/feature-libraries/forest-summary/codec.ts +8 -7
- package/src/feature-libraries/forest-summary/formatCommon.ts +5 -3
- package/src/feature-libraries/forest-summary/formatV1.ts +1 -3
- package/src/feature-libraries/forest-summary/formatV2.ts +1 -3
- package/src/feature-libraries/modular-schema/modularChangeCodecV1.ts +5 -6
- package/src/feature-libraries/schema-index/codec.ts +6 -4
- package/src/index.ts +3 -3
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/sharedTreeChangeCodecs.ts +2 -2
- package/src/shared-tree/tree.ts +1 -1
- package/src/shared-tree/treeAlpha.ts +1 -1
- package/src/shared-tree/treeCheckout.ts +2 -4
- package/src/shared-tree-core/editManagerCodecsCommons.ts +7 -7
- package/src/shared-tree-core/editManagerCodecsV1toV4.ts +3 -10
- package/src/shared-tree-core/editManagerCodecsVSharedBranches.ts +3 -10
- package/src/shared-tree-core/messageCodecs.ts +2 -6
- package/src/simple-tree/api/index.ts +2 -2
- package/src/simple-tree/api/snapshotCompatibilityChecker.ts +344 -142
- package/src/simple-tree/api/tree.ts +1 -1
- package/src/simple-tree/api/treeBeta.ts +1 -1
- package/src/simple-tree/core/allowedTypes.ts +1 -1
- package/src/simple-tree/core/unhydratedFlexTree.ts +43 -1
- package/src/simple-tree/index.ts +2 -2
- package/src/simple-tree/node-kinds/array/arrayNode.ts +13 -19
- package/src/simple-tree/unhydratedFlexTreeFromInsertable.ts +51 -10
- package/src/text/textDomainFormatted.ts +37 -17
- package/src/util/bTreeUtils.ts +10 -6
- package/src/util/rangeMap.ts +9 -6
- package/api-extractor-lint.json +0 -4
- package/dist/extensibleSchemaUnion.d.ts +0 -72
- package/dist/extensibleSchemaUnion.d.ts.map +0 -1
- package/dist/extensibleSchemaUnion.js.map +0 -1
- package/lib/extensibleSchemaUnion.d.ts +0 -72
- package/lib/extensibleSchemaUnion.d.ts.map +0 -1
- package/lib/extensibleSchemaUnion.js.map +0 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import { assert, transformMapValues } from "@fluidframework/core-utils/internal";
|
|
5
|
+
import { assert, fail, transformMapValues } from "@fluidframework/core-utils/internal";
|
|
6
6
|
import { selectVersionRoundedDown } from "@fluidframework/runtime-utils/internal";
|
|
7
7
|
import { UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
8
8
|
import * as semver from "semver-ts";
|
|
@@ -131,7 +131,7 @@ export function importCompatibilitySchemaSnapshot(config) {
|
|
|
131
131
|
/**
|
|
132
132
|
* Check `currentViewSchema` for compatibility with a collection of historical schema snapshots stored in `snapshotDirectory`.
|
|
133
133
|
*
|
|
134
|
-
* @throws Throws errors if the input version strings (including those in snapshot file names) are not valid semver versions.
|
|
134
|
+
* @throws Throws errors if the input version strings (including those in snapshot file names) are not valid semver versions when using default semver version comparison.
|
|
135
135
|
* @throws Throws errors if the input version strings (including those in snapshot file names) are not ordered as expected (current being the highest, and `minVersionForCollaboration` corresponding to the current version or a lower snapshotted version).
|
|
136
136
|
* @throws In `test` mode, throws an error if there is not an up to date snapshot for the current version.
|
|
137
137
|
* @throws Throws an error if any snapshotted schema cannot be upgraded to the current schema.
|
|
@@ -151,6 +151,14 @@ export function importCompatibilitySchemaSnapshot(config) {
|
|
|
151
151
|
* This is a known limitation that will be improved in future releases.
|
|
152
152
|
* These improvements, as well as other changes, may change the exact messages produced by this function in the error cases: no stability of these messages should be assumed.
|
|
153
153
|
*
|
|
154
|
+
* Unlike some other snapshot based testing tools, this stores more than just the current snapshot: historical snapshots are retained as well.
|
|
155
|
+
* Retention of these additional historical snapshots, whose content can't be regenerated from the current schema, is necessary to properly test compatibility across versions.
|
|
156
|
+
* Since there is content in the snapshots which cannot be regenerated, tools which assume all snapshotted content can be regenerated cannot be used here.
|
|
157
|
+
* This means that tools like Jest's built in snapshot testing are not suitable for this purpose.
|
|
158
|
+
* These snapshots behave partly like test data, and partly like snapshots.
|
|
159
|
+
* Typically the easiest way to manage this is to place {@link SnapshotSchemaCompatibilityOptions.snapshotDirectory} inside a directory appropriate for test data,
|
|
160
|
+
* and use node to provide the filesystem access via {@link SnapshotSchemaCompatibilityOptions.fileSystem}.
|
|
161
|
+
*
|
|
154
162
|
* For now, locating what change broke compatibility is likely best discovered by making small schema changes one at a time and updating the snapshot and reviewing the diffs.
|
|
155
163
|
* Details for what kinds of changes are breaking and in which ways can be found in the documentation for {@link TreeView.compatibility} and
|
|
156
164
|
* {@link https://fluidframework.com/docs/data-structures/tree/schema-evolution/ | schema-evolution}.
|
|
@@ -169,7 +177,7 @@ export function importCompatibilitySchemaSnapshot(config) {
|
|
|
169
177
|
* @example Mocha test which validates the current `config` can collaborate with all historical version back to 2.0.0, and load and update any versions older than that.
|
|
170
178
|
* ```typescript
|
|
171
179
|
* it("schema compatibility", () => {
|
|
172
|
-
*
|
|
180
|
+
* snapshotSchemaCompatibility({
|
|
173
181
|
* version: pkgVersion,
|
|
174
182
|
* schema: config,
|
|
175
183
|
* fileSystem: { ...fs, ...path },
|
|
@@ -179,148 +187,213 @@ export function importCompatibilitySchemaSnapshot(config) {
|
|
|
179
187
|
* });
|
|
180
188
|
* });
|
|
181
189
|
* ```
|
|
190
|
+
* @example Complete Mocha test file
|
|
191
|
+
* ```typescript
|
|
192
|
+
* import fs from "node:fs";
|
|
193
|
+
* import path from "node:path";
|
|
194
|
+
*
|
|
195
|
+
* import { snapshotSchemaCompatibility } from "@fluidframework/tree/alpha";
|
|
196
|
+
*
|
|
197
|
+
* // The TreeViewConfiguration the application uses, which contains the application's schema.
|
|
198
|
+
* import { treeViewConfiguration } from "./schema.js";
|
|
199
|
+
* // The next version of the application which will be released.
|
|
200
|
+
* import { packageVersion } from "./version.js";
|
|
201
|
+
*
|
|
202
|
+
* // Provide some way to run the check in "update" mode when updating snapshots is intended.
|
|
203
|
+
* const regenerateSnapshots = process.argv.includes("--snapshot");
|
|
204
|
+
*
|
|
205
|
+
* // Setup the actual test. In this case using Mocha syntax.
|
|
206
|
+
* describe("schema", () => {
|
|
207
|
+
* it("schema compatibility", () => {
|
|
208
|
+
* // Select a path to save the snapshots in.
|
|
209
|
+
* // This will depend on how your application organizes its test data.
|
|
210
|
+
* const snapshotDirectory = path.join(
|
|
211
|
+
* import.meta.dirname,
|
|
212
|
+
* "../../../src/test/schema-snapshots",
|
|
213
|
+
* );
|
|
214
|
+
* snapshotSchemaCompatibility({
|
|
215
|
+
* schema: config,
|
|
216
|
+
* fileSystem: { ...fs, ...path },
|
|
217
|
+
* version: pkgVersion,
|
|
218
|
+
* minVersionForCollaboration: "2.0.0",
|
|
219
|
+
* mode: process.argv.includes("--snapshot") ? "update" : "assert",
|
|
220
|
+
* snapshotDirectory,
|
|
221
|
+
* });
|
|
222
|
+
* });
|
|
223
|
+
* });
|
|
224
|
+
* ```
|
|
182
225
|
* @privateRemarks
|
|
183
226
|
* Use of this function within this package (for schema libraries released as part of this package) should use {@link testSchemaCompatibilitySnapshots} instead.
|
|
184
227
|
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* This should be addressed before this reached beta stability.
|
|
228
|
+
* This uses the format defined in simpleSchemaCodec.ts.
|
|
229
|
+
* This does include versioning information in the snapshot format,
|
|
230
|
+
* but it would be nice to better unify how we do that versioning and format validation with our codecs.
|
|
231
|
+
*
|
|
232
|
+
* See snapshotCompatibilityChecker.example.mts for the large example included above.
|
|
191
233
|
* @alpha
|
|
192
234
|
*/
|
|
193
|
-
export function
|
|
235
|
+
export function snapshotSchemaCompatibility(options) {
|
|
194
236
|
const checker = new SnapshotCompatibilityChecker(options.snapshotDirectory, options.fileSystem);
|
|
195
|
-
const { version: currentVersion, schema: currentViewSchema, mode, minVersionForCollaboration, snapshotUnchangedVersions, } = options;
|
|
196
|
-
|
|
237
|
+
const { version: currentVersion, schema: currentViewSchema, mode, minVersionForCollaboration, snapshotUnchangedVersions, rejectVersionsWithNoSchemaChange, rejectSchemaChangesWithNoVersionChange, } = options;
|
|
238
|
+
const validateVersion = options.versionComparer === undefined ? semver.valid : (v) => v;
|
|
239
|
+
const versionComparer = options.versionComparer ?? semver.compare;
|
|
240
|
+
if (validateVersion(currentVersion) === null) {
|
|
197
241
|
throw new UsageError(`Invalid version: ${JSON.stringify(currentVersion)}. Must be a valid semver version.`);
|
|
198
242
|
}
|
|
199
|
-
if (
|
|
243
|
+
if (validateVersion(minVersionForCollaboration) === null) {
|
|
200
244
|
throw new UsageError(`Invalid minVersionForCollaboration: ${JSON.stringify(minVersionForCollaboration)}. Must be a valid semver version.`);
|
|
201
245
|
}
|
|
202
|
-
if (
|
|
246
|
+
if (versionComparer(minVersionForCollaboration, currentVersion) > 0) {
|
|
203
247
|
throw new UsageError(`Invalid minVersionForCollaboration: ${JSON.stringify(minVersionForCollaboration)}. Must be less than or equal to current version ${JSON.stringify(currentVersion)}.`);
|
|
204
248
|
}
|
|
205
|
-
if (mode !== "
|
|
206
|
-
throw new UsageError(`Invalid mode: ${JSON.stringify(mode)}. Must be either "
|
|
249
|
+
if (mode !== "assert" && mode !== "update") {
|
|
250
|
+
throw new UsageError(`Invalid mode: ${JSON.stringify(mode)}. Must be either "assert" or "update".`);
|
|
207
251
|
}
|
|
208
252
|
const currentEncodedForSnapshotting = exportCompatibilitySchemaSnapshot(currentViewSchema);
|
|
209
|
-
const snapshots = checker.readAllSchemaSnapshots();
|
|
253
|
+
const snapshots = checker.readAllSchemaSnapshots(versionComparer);
|
|
210
254
|
const compatibilityErrors = [];
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
255
|
+
const contextNotes = [];
|
|
256
|
+
function errorWithContext(message) {
|
|
257
|
+
return new Error([
|
|
258
|
+
"Schema compatibility check failed:",
|
|
259
|
+
message,
|
|
260
|
+
`Snapshots in: ${JSON.stringify(options.snapshotDirectory)}`,
|
|
261
|
+
`Snapshots exist for versions: ${JSON.stringify([...snapshots.keys()], undefined, "\t")}.`,
|
|
262
|
+
...contextNotes,
|
|
263
|
+
].join("\n"));
|
|
214
264
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
265
|
+
const compatibilityMap = transformMapValues(snapshots, (snapshot) => getCompatibility(currentViewSchema, snapshot));
|
|
266
|
+
// Either:
|
|
267
|
+
// - false: no update needed
|
|
268
|
+
// - the updateError message (update in update mode, error otherwise)
|
|
269
|
+
// - an error if the update is disallowed by the flags
|
|
270
|
+
let wouldUpdate;
|
|
271
|
+
// Set wouldUpdate
|
|
272
|
+
{
|
|
273
|
+
const latestSnapshot = [...snapshots][snapshots.size - 1];
|
|
274
|
+
if (latestSnapshot === undefined) {
|
|
275
|
+
wouldUpdate = `No snapshots found.`;
|
|
219
276
|
}
|
|
220
277
|
else {
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
278
|
+
const latestCompatibility = compatibilityMap.get(latestSnapshot[0]) ??
|
|
279
|
+
fail(0xcd1 /* missing compatibilityMap entry */);
|
|
280
|
+
const schemaChange = !latestCompatibility.identicalCompatibility;
|
|
281
|
+
const versionChange = versionComparer(latestSnapshot[0], currentVersion) !== 0;
|
|
282
|
+
if (rejectVersionsWithNoSchemaChange === true && versionChange && !schemaChange) {
|
|
283
|
+
wouldUpdate = errorWithContext(`Rejecting version change (${JSON.stringify(latestSnapshot[0])} to ${JSON.stringify(currentVersion)}) due to rejectVersionsWithNoSchemaChange being set.`);
|
|
224
284
|
}
|
|
225
|
-
else if (
|
|
226
|
-
|
|
227
|
-
|
|
285
|
+
else if (rejectSchemaChangesWithNoVersionChange === true &&
|
|
286
|
+
schemaChange &&
|
|
287
|
+
!versionChange) {
|
|
288
|
+
wouldUpdate = errorWithContext(`Rejecting schema change without version change due to existing non-equivalent snapshot for version (${JSON.stringify(latestSnapshot[0])} due to rejectSchemaChangesWithNoVersionChange being set.`);
|
|
228
289
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
290
|
+
else if (snapshotUnchangedVersions === true) {
|
|
291
|
+
const currentRead = snapshots.get(currentVersion);
|
|
292
|
+
if (currentRead === undefined) {
|
|
293
|
+
wouldUpdate = `No snapshot found for version ${JSON.stringify(currentVersion)}: snapshotUnchangedVersions is true, so every version must be snapshotted.`;
|
|
294
|
+
}
|
|
295
|
+
else if (JSON.stringify(exportCompatibilitySchemaSnapshot(currentRead)) ===
|
|
296
|
+
JSON.stringify(currentEncodedForSnapshotting)) {
|
|
297
|
+
wouldUpdate = false;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
wouldUpdate = `Snapshot for current version ${JSON.stringify(currentVersion)} is out of date.`;
|
|
301
|
+
}
|
|
238
302
|
}
|
|
239
303
|
else {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const currentString = JSON.stringify(currentEncodedForSnapshotting);
|
|
248
|
-
if (oldString !== currentString) {
|
|
249
|
-
// Schema has changed: must create new snapshot.
|
|
250
|
-
if (mode === "update") {
|
|
251
|
-
checker.writeSchemaSnapshot(currentVersion, currentEncodedForSnapshotting);
|
|
252
|
-
snapshots.set(currentVersion, currentViewSchema);
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
updatableError(`Snapshot for current version ${JSON.stringify(currentVersion)} is out of date: schema has changed since latest existing snapshot version ${JSON.stringify(latestSnapshot[0])}.`);
|
|
256
|
-
}
|
|
304
|
+
if (versionComparer(latestSnapshot[0], currentVersion) <= 0) {
|
|
305
|
+
wouldUpdate = schemaChange
|
|
306
|
+
? `Snapshot for current version ${JSON.stringify(currentVersion)} is out of date: schema has changed since latest existing snapshot version ${JSON.stringify(latestSnapshot[0])}.`
|
|
307
|
+
: false;
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
wouldUpdate = errorWithContext(`Current version ${JSON.stringify(currentVersion)} is less than latest existing snapshot version ${JSON.stringify(latestSnapshot[0])}: version is expected to increase monotonically.`);
|
|
257
311
|
}
|
|
258
312
|
}
|
|
259
|
-
|
|
260
|
-
|
|
313
|
+
if (!schemaChange && (snapshotUnchangedVersions !== true || !versionChange)) {
|
|
314
|
+
// eslint-disable-next-line unicorn/no-lonely-if
|
|
315
|
+
if (JSON.stringify(exportCompatibilitySchemaSnapshot(latestSnapshot[1])) !==
|
|
316
|
+
JSON.stringify(currentEncodedForSnapshotting)) {
|
|
317
|
+
// Schema are compatibility wise equivalent, but differ in some way (excluding json formatting).
|
|
318
|
+
// TODO: add a "normalize" mode, which do an update only in this case (or maybe even normalize json formatting as well and just always rewrite when !schemaChange)
|
|
319
|
+
// This would be useful to minimize diffs from future schema changes.
|
|
320
|
+
// This would be particularly useful if adding a second version of the format used in the snapshots.
|
|
321
|
+
}
|
|
261
322
|
}
|
|
262
323
|
}
|
|
263
324
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (
|
|
269
|
-
|
|
325
|
+
if (wouldUpdate !== false) {
|
|
326
|
+
if (wouldUpdate instanceof Error) {
|
|
327
|
+
throw wouldUpdate;
|
|
328
|
+
}
|
|
329
|
+
if (mode === "update") {
|
|
330
|
+
checker.writeSchemaSnapshot(currentVersion, currentEncodedForSnapshotting);
|
|
331
|
+
// Update so errors below will reflect the new snapshot.
|
|
332
|
+
compatibilityMap.set(currentVersion, getCompatibility(currentViewSchema, currentViewSchema));
|
|
270
333
|
}
|
|
271
334
|
else {
|
|
272
|
-
|
|
335
|
+
compatibilityErrors.push(`${wouldUpdate} If this is expected, snapshotSchemaCompatibility can be rerun in "update" mode to update or create the snapshot.`);
|
|
336
|
+
// This case could update compatibilityMap as well, but it would hide some information about how the existing snapshot might be incompatible with the proposed new one.
|
|
337
|
+
// This lost information could be annoying if the user's intention was not to edit the schema (which is what we assume in assert mode),
|
|
338
|
+
// especially once we produce more detailed error messages that can help users understand what changed in the schema.
|
|
273
339
|
}
|
|
274
340
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
341
|
+
// Add compatibilityErrors and contextNotes as needed regarding minVersionForCollaboration.
|
|
342
|
+
// This is only done when minVersionForCollaboration is not the current version to avoid extra noise in "assert" mode
|
|
343
|
+
// (which is the only case that could error when minVersionForCollaboration === currentVersion).
|
|
344
|
+
if (minVersionForCollaboration !== currentVersion) {
|
|
345
|
+
if (snapshotUnchangedVersions === true) {
|
|
346
|
+
const minSnapshot = compatibilityMap.get(minVersionForCollaboration);
|
|
347
|
+
if (minSnapshot === undefined) {
|
|
348
|
+
compatibilityErrors.push(`Using snapshotUnchangedVersions: a snapshot of the exact minVersionForCollaboration ${JSON.stringify(minVersionForCollaboration)} is required. No snapshot found.`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
const selectedMinVersionForCollaborationSnapshot = selectVersionRoundedDown(minVersionForCollaboration, compatibilityMap, versionComparer);
|
|
353
|
+
if (selectedMinVersionForCollaborationSnapshot === undefined) {
|
|
354
|
+
compatibilityErrors.push(`No snapshot found with version less than or equal to minVersionForCollaboration ${JSON.stringify(minVersionForCollaboration)}.`);
|
|
355
|
+
}
|
|
356
|
+
else if (selectedMinVersionForCollaborationSnapshot[0] !== minVersionForCollaboration) {
|
|
357
|
+
// Add an entry to ensure that the version which spans from before until after the cutoff for collaboration is included in the compatibility checks.
|
|
358
|
+
compatibilityMap.set(minVersionForCollaboration, selectedMinVersionForCollaborationSnapshot[1]);
|
|
359
|
+
contextNotes.push(`Due to snapshotUnchangedVersions being false and minVersionForCollaboration (${JSON.stringify(minVersionForCollaboration)}) not having an exact snapshot, the last snapshot before that version (which is ${JSON.stringify(selectedMinVersionForCollaborationSnapshot[0])}) is being also being checked as if it is version ${JSON.stringify(minVersionForCollaboration)}.`);
|
|
360
|
+
}
|
|
279
361
|
}
|
|
280
362
|
}
|
|
363
|
+
// Compare all snapshots against the current schema, using the compatibilityMap.
|
|
281
364
|
for (const [snapshotVersion, compatibility] of compatibilityMap) {
|
|
282
365
|
// Current should be able to view all versions.
|
|
283
366
|
if (!compatibility.currentViewOfSnapshotDocument.canUpgrade) {
|
|
284
367
|
compatibilityErrors.push(`Current version ${JSON.stringify(currentVersion)} cannot upgrade documents from ${JSON.stringify(snapshotVersion)}.`);
|
|
285
368
|
}
|
|
286
|
-
|
|
369
|
+
const versionComparisonToCurrent = versionComparer(snapshotVersion, currentVersion);
|
|
370
|
+
if (versionComparisonToCurrent === 0) {
|
|
287
371
|
if (currentVersion !== snapshotVersion) {
|
|
288
|
-
throw
|
|
372
|
+
throw errorWithContext(`Snapshot version ${JSON.stringify(snapshotVersion)} is semantically equal but not string equal to current version ${JSON.stringify(currentVersion)}: this is not supported.`);
|
|
289
373
|
}
|
|
290
|
-
if (compatibility.
|
|
291
|
-
|
|
292
|
-
compatibilityErrors.push(`Current version ${JSON.stringify(snapshotVersion)} expected to be equivalent to its snapshot.`);
|
|
374
|
+
if (compatibility.identicalCompatibility === false) {
|
|
375
|
+
assert(wouldUpdate !== false, 0xcd2 /* there should have been an error for the snapshot being out of date */);
|
|
293
376
|
}
|
|
294
377
|
}
|
|
295
|
-
else if (
|
|
296
|
-
|
|
297
|
-
|
|
378
|
+
else if (versionComparisonToCurrent < 0) {
|
|
379
|
+
// Collaboration with this version is expected to work.
|
|
380
|
+
if (versionComparer(snapshotVersion, minVersionForCollaboration) >= 0) {
|
|
381
|
+
// Check that the historical version can view documents from the current version, since collaboration with this one is expected to work.
|
|
382
|
+
if (!compatibility.snapshotViewOfCurrentDocument.canView) {
|
|
383
|
+
compatibilityErrors.push(`Historical version ${JSON.stringify(snapshotVersion)} cannot view documents from ${JSON.stringify(currentVersion)}: these versions are expected to be able to collaborate due to the selected minVersionForCollaboration ${JSON.stringify(minVersionForCollaboration)}.`);
|
|
384
|
+
}
|
|
298
385
|
}
|
|
299
386
|
else {
|
|
300
|
-
//
|
|
301
|
-
|
|
302
|
-
// Check that the historical version can view documents from the current version, since collaboration with this one is expected to work.
|
|
303
|
-
if (!compatibility.snapshotViewOfCurrentDocument.canView) {
|
|
304
|
-
const message = `Historical version ${JSON.stringify(snapshotVersion)} cannot view documents from ${JSON.stringify(currentVersion)}: these versions are expected to be able to collaborate due to the selected minVersionForCollaboration snapshot version being ${JSON.stringify(selectedMinVersionForCollaborationSnapshot[0])}.`;
|
|
305
|
-
compatibilityErrors.push(selectedMinVersionForCollaborationSnapshot[0] === minVersionForCollaboration
|
|
306
|
-
? message
|
|
307
|
-
: `${message} The specified minVersionForCollaboration is ${JSON.stringify(minVersionForCollaboration)} which was rounded down to an existing snapshot.`);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
// This is the case where the historical version is less than the minimum version for collaboration.
|
|
312
|
-
// No additional validation is needed here currently, since forwards document compat from these versions is already tested above (since it applies to all snapshots).
|
|
313
|
-
}
|
|
387
|
+
// This is the case where the historical version is less than the minimum version for collaboration.
|
|
388
|
+
// No additional validation is needed here currently, since forwards document compat from these versions is already tested above (since it applies to all snapshots).
|
|
314
389
|
}
|
|
315
390
|
}
|
|
316
391
|
else {
|
|
317
|
-
|
|
392
|
+
compatibilityErrors.push(`Snapshot exists for version ${JSON.stringify(snapshotVersion)} which is greater than the current version ${JSON.stringify(currentVersion)}. This is not supported.`);
|
|
318
393
|
}
|
|
319
394
|
}
|
|
320
395
|
if (compatibilityErrors.length > 0) {
|
|
321
|
-
throw
|
|
322
|
-
.map((e) => ` - ${e}`)
|
|
323
|
-
.join("\n")}`);
|
|
396
|
+
throw errorWithContext(compatibilityErrors.map((e) => ` - ${e}`).join("\n"));
|
|
324
397
|
}
|
|
325
398
|
}
|
|
326
399
|
/**
|
|
@@ -342,7 +415,7 @@ export class SnapshotCompatibilityChecker {
|
|
|
342
415
|
writeSchemaSnapshot(snapshotName, snapshot) {
|
|
343
416
|
const fullPath = this.fileSystemMethods.join(this.snapshotDirectory, `${snapshotName}.json`);
|
|
344
417
|
this.ensureSnapshotDirectoryExists();
|
|
345
|
-
this.fileSystemMethods.writeFileSync(fullPath, JSON.stringify(snapshot, undefined,
|
|
418
|
+
this.fileSystemMethods.writeFileSync(fullPath, JSON.stringify(snapshot, undefined, "\t"), {
|
|
346
419
|
encoding: "utf8",
|
|
347
420
|
});
|
|
348
421
|
}
|
|
@@ -358,7 +431,7 @@ export class SnapshotCompatibilityChecker {
|
|
|
358
431
|
/**
|
|
359
432
|
* Returns all schema snapshots stored in the snapshot directory, sorted in order of increasing version.
|
|
360
433
|
*/
|
|
361
|
-
readAllSchemaSnapshots() {
|
|
434
|
+
readAllSchemaSnapshots(compare) {
|
|
362
435
|
this.ensureSnapshotDirectoryExists();
|
|
363
436
|
const files = this.fileSystemMethods.readdirSync(this.snapshotDirectory);
|
|
364
437
|
const versions = [];
|
|
@@ -369,7 +442,7 @@ export class SnapshotCompatibilityChecker {
|
|
|
369
442
|
}
|
|
370
443
|
}
|
|
371
444
|
// Ensures that errors are in a consistent and friendly order, independent of file system order.
|
|
372
|
-
versions.sort(
|
|
445
|
+
versions.sort(compare);
|
|
373
446
|
const snapshots = new Map();
|
|
374
447
|
for (const version of versions) {
|
|
375
448
|
snapshots.set(version, this.readSchemaSnapshot(version));
|
|
@@ -389,9 +462,17 @@ export class SnapshotCompatibilityChecker {
|
|
|
389
462
|
export function getCompatibility(currentViewSchema, previousViewSchema) {
|
|
390
463
|
const backwardsCompatibilityStatus = checkCompatibility(previousViewSchema, currentViewSchema);
|
|
391
464
|
const forwardsCompatibilityStatus = checkCompatibility(currentViewSchema, previousViewSchema);
|
|
465
|
+
assert(backwardsCompatibilityStatus.isEquivalent === forwardsCompatibilityStatus.isEquivalent, 0xcd3 /* equality should be symmetric */);
|
|
466
|
+
// This relies on exportCompatibilitySchemaSnapshot being well normalized, and not differing for non-significant changes.
|
|
467
|
+
const identicalCompatibility = JSON.stringify(exportCompatibilitySchemaSnapshot(currentViewSchema)) ===
|
|
468
|
+
JSON.stringify(exportCompatibilitySchemaSnapshot(previousViewSchema));
|
|
469
|
+
if (identicalCompatibility) {
|
|
470
|
+
assert(backwardsCompatibilityStatus.isEquivalent, 0xcd4 /* identicalCompatibility should have equivalent stored schema */);
|
|
471
|
+
}
|
|
392
472
|
return {
|
|
393
473
|
currentViewOfSnapshotDocument: backwardsCompatibilityStatus,
|
|
394
474
|
snapshotViewOfCurrentDocument: forwardsCompatibilityStatus,
|
|
475
|
+
identicalCompatibility,
|
|
395
476
|
};
|
|
396
477
|
}
|
|
397
478
|
//# sourceMappingURL=snapshotCompatibilityChecker.js.map
|