fluid-framework 2.72.0 → 2.73.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 CHANGED
@@ -1,5 +1,104 @@
1
1
  # fluid-framework
2
2
 
3
+ ## 2.73.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Schema snapshot compatibility checker ([#25861](https://github.com/microsoft/FluidFramework/pull/25861)) [e5be416321](https://github.com/microsoft/FluidFramework/commit/e5be4163210ef68b7f8a7c10502f4871c30ec9f3)
8
+
9
+ This change adds alpha APIs for creating snapshots of view schema and testing their compatibility for the purposes
10
+ of schema migrations.
11
+
12
+ New APIs:
13
+ - `checkCompatibility` - Checks the compatibility of the view schema which created the document against the view schema
14
+ being used to open it.
15
+ - `importCompatibilitySchemaSnapshot` - Parse a JSON representation of a tree schema into a concrete schema.
16
+ - `exportCompatibilitySchemaSnapshot` - Returns a JSON representation of the tree schema for snapshot compatibility checking.
17
+
18
+ #### Example: Current view schema vs. historical view schema
19
+
20
+ An application author is developing an app that has a schema for storing 2D Points.
21
+ They wish to maintain backwards compatibility in future versions and avoid changing their view schema in a way that breaks
22
+ this behavior.
23
+ When introducing a new initial schema, they persists a snapshot using `exportCompatibilitySchemaSnapshot`:
24
+
25
+ ```ts
26
+ const factory = new SchemaFactory("test");
27
+
28
+ // The past view schema, for the purposes of illustration. This wouldn't normally appear as a concrete schema in the test
29
+ // checking compatibility, but rather would be loaded from a snapshot.
30
+ class Point2D extends factory.object("Point", {
31
+ x: factory.number,
32
+ y: factory.number,
33
+ }) {}
34
+ const viewSchema = new TreeViewConfiguration({ schema: Point2D });
35
+ const encodedSchema = JSON.stringify(
36
+ exportCompatibilitySchemaSnapshot(viewSchema),
37
+ );
38
+ fs.writeFileSync("PointSchema.json", encodedSchema);
39
+ ```
40
+
41
+ Next they create a regression test to ensure that the current view schema can read content written by the original view
42
+ schema (`SchemaCompatibilityStatus.canUpgrade`). Initially `currentViewSchema === Point2D`:
43
+
44
+ ```ts
45
+ const encodedSchema = JSON.parse(fs.readFileSync("PointSchema.json", "utf8"));
46
+ const oldViewSchema = importCompatibilitySchemaSnapshot(encodedSchema);
47
+
48
+ // Check to see if the document created by the historical view schema can be opened with the current view schema
49
+ const compatibilityStatus = checkCompatibility(
50
+ oldViewSchema,
51
+ currentViewSchema,
52
+ );
53
+
54
+ // Check to see if the document created by the historical view schema can be opened with the current view schema
55
+ const backwardsCompatibilityStatus = checkCompatibility(
56
+ oldViewSchema,
57
+ currentViewSchema,
58
+ );
59
+
60
+ // z is not present in Point2D, so the schema must be upgraded
61
+ assert.equal(backwardsCompatibilityStatus.canView, false);
62
+
63
+ // The schema can be upgraded to add the new optional field
64
+ assert.equal(backwardsCompatibilityStatus.canUpgrade, true);
65
+ ```
66
+
67
+ Additionally, they a regression test to ensure that older view schemas can read content written by the current view
68
+ schema (`SchemaCompatibilityStatus.canView`):
69
+
70
+ ```ts
71
+ // Test what the old version of the application would do with a tree using the new schema:
72
+ const forwardsCompatibilityStatus = checkCompatibility(
73
+ currentViewSchema,
74
+ oldViewSchema,
75
+ );
76
+
77
+ // If the old schema set allowUnknownOptionalFields, this would be true, but since it did not,
78
+ // this assert will fail, detecting the forwards compatibility break:
79
+ // this means these two versions of the application cannot collaborate on content using these schema.
80
+ assert.equal(forwardsCompatibilityStatus.canView, true);
81
+ ```
82
+
83
+ Later in the application development cycle, the application author decides they want to change their Point2D to
84
+ a Point3D, adding an extra field:
85
+
86
+ ```ts
87
+ // Build the current view schema
88
+ const schemaFactory = new SchemaFactory("test");
89
+ class Point3D extends schemaFactory.object("Point", {
90
+ x: factory.number,
91
+ y: factory.number,
92
+
93
+ // The current schema has a new optional field that was not present on Point2D
94
+ z: factory.optional(factory.number),
95
+ }) {}
96
+ ```
97
+
98
+ The test first compatibility test will pass as the Point2D schema is upgradeable to a Point3D schema.
99
+ However, the second compatibility test fill fail as an application using the Point2D view schema cannot collaborate on
100
+ content authored using the Point3D schema.
101
+
3
102
  ## 2.72.0
4
103
 
5
104
  ### Minor Changes
@@ -117,6 +117,9 @@ export const ArrayNodeSchema: {
117
117
  // @alpha
118
118
  export function asAlpha<TSchema extends ImplicitFieldSchema>(view: TreeView<TSchema>): TreeViewAlpha<TSchema>;
119
119
 
120
+ // @alpha
121
+ export function asAlpha<TSchema extends ImplicitFieldSchema>(view: TreeViewConfiguration<TSchema>): TreeViewConfigurationAlpha<TSchema>;
122
+
120
123
  // @beta
121
124
  export function asBeta<TSchema extends ImplicitFieldSchema>(view: TreeView<TSchema>): TreeViewBeta<TSchema>;
122
125
 
@@ -138,6 +141,9 @@ export interface BranchableTree extends ViewableTree {
138
141
  rebase(branch: TreeBranchFork): void;
139
142
  }
140
143
 
144
+ // @alpha
145
+ export function checkCompatibility(viewWhichCreatedStoredSchema: TreeViewConfiguration, view: TreeViewConfiguration): Omit<SchemaCompatibilityStatus, "canInitialize">;
146
+
141
147
  // @alpha
142
148
  export function cloneWithReplacements(root: unknown, rootKey: string, replacer: (key: string, value: unknown) => {
143
149
  clone: boolean;
@@ -287,6 +293,9 @@ export abstract class ErasedType<out Name = unknown> {
287
293
  // @alpha
288
294
  export function evaluateLazySchema<T extends TreeNodeSchema>(value: LazyItem<T>): T;
289
295
 
296
+ // @alpha
297
+ export function exportCompatibilitySchemaSnapshot(config: Pick<TreeViewConfiguration, "schema">): JsonCompatibleReadOnly;
298
+
290
299
  // @public @system
291
300
  type ExtractItemType<Item extends LazyItem> = Item extends () => infer Result ? Result : Item;
292
301
 
@@ -382,6 +391,7 @@ export const FluidClientVersion: {
382
391
  readonly v2_0: "2.0.0";
383
392
  readonly v2_43: "2.43.0";
384
393
  readonly v2_52: "2.52.0";
394
+ readonly v2_73: "2.73.0";
385
395
  };
386
396
 
387
397
  // @public
@@ -728,6 +738,9 @@ export type ImplicitAllowedTypes = AllowedTypes | TreeNodeSchema;
728
738
  // @public
729
739
  export type ImplicitFieldSchema = FieldSchema | ImplicitAllowedTypes;
730
740
 
741
+ // @alpha
742
+ export function importCompatibilitySchemaSnapshot(config: JsonCompatibleReadOnly): TreeViewConfiguration;
743
+
731
744
  // @alpha
732
745
  export function independentInitializedView<const TSchema extends ImplicitFieldSchema>(config: TreeViewConfiguration<TSchema>, options: ForestOptions & ICodecOptions, content: ViewContent): TreeViewAlpha<TSchema>;
733
746
 
@@ -1387,18 +1400,6 @@ export interface SharedTreeFormatOptions {
1387
1400
  treeEncodeType: TreeCompressionStrategy;
1388
1401
  }
1389
1402
 
1390
- // @alpha
1391
- export const SharedTreeFormatVersion: {
1392
- readonly v1: 1;
1393
- readonly v2: 2;
1394
- readonly v3: 3;
1395
- readonly v5: 5;
1396
- readonly vSharedBranches: 100;
1397
- };
1398
-
1399
- // @alpha
1400
- export type SharedTreeFormatVersion = typeof SharedTreeFormatVersion;
1401
-
1402
1403
  // @alpha @input
1403
1404
  export interface SharedTreeOptions extends Partial<CodecWriteOptions>, Partial<SharedTreeFormatOptions>, SharedTreeOptionsBeta {
1404
1405
  readonly enableSharedBranches?: boolean;
@@ -1409,7 +1410,7 @@ export type SharedTreeOptionsBeta = ForestOptions;
1409
1410
 
1410
1411
  // @alpha @sealed
1411
1412
  export interface SimpleAllowedTypeAttributes {
1412
- readonly isStaged: boolean | undefined;
1413
+ readonly isStaged: false | SchemaUpgrade | undefined;
1413
1414
  }
1414
1415
 
1415
1416
  // @alpha @sealed
@@ -1486,7 +1487,7 @@ export namespace System_TableSchema {
1486
1487
  // @system
1487
1488
  export type CreateColumnOptionsBase<TSchemaFactory extends SchemaFactoryBeta = SchemaFactoryBeta, TCell extends ImplicitAllowedTypes = ImplicitAllowedTypes> = OptionsWithSchemaFactory<TSchemaFactory> & OptionsWithCellSchema<TCell>;
1488
1489
  // @system
1489
- export function createColumnSchema<const TInputScope extends string | undefined, const TCellSchema extends ImplicitAllowedTypes, const TPropsSchema extends ImplicitFieldSchema>(inputSchemaFactory: SchemaFactoryBeta<TInputScope>, cellSchema: TCellSchema, propsSchema: TPropsSchema): TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Column">, NodeKind.Object, TreeNode & TableSchema.Column<TCellSchema, TPropsSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Column">, NodeKind, unknown>, object & {
1490
+ export function createColumnSchema<const TInputScope extends string | undefined, const TCellSchema extends ImplicitAllowedTypes, const TPropsSchema extends ImplicitFieldSchema>(inputSchemaFactory: SchemaFactoryBeta<TInputScope>, propsSchema: TPropsSchema): TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Column">, NodeKind.Object, TreeNode & TableSchema.Column<TCellSchema, TPropsSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Column">, NodeKind, unknown>, object & {
1490
1491
  readonly id?: string | undefined;
1491
1492
  } & (FieldHasDefault<TPropsSchema> extends true ? {
1492
1493
  props?: InsertableTreeFieldFromImplicitField<TPropsSchema> | undefined;
@@ -1501,7 +1502,7 @@ export namespace System_TableSchema {
1501
1502
  // @sealed
1502
1503
  export function createRowSchema<const TInputScope extends string | undefined, const TCellSchema extends ImplicitAllowedTypes, const TPropsSchema extends ImplicitFieldSchema>(inputSchemaFactory: SchemaFactoryBeta<TInputScope>, cellSchema: TCellSchema, propsSchema: TPropsSchema): TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row">, NodeKind.Object, TreeNode & TableSchema.Row<TCellSchema, TPropsSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row">, NodeKind, unknown>, object & {
1503
1504
  readonly id?: string | undefined;
1504
- readonly cells: (InsertableTypedNode_2<TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, TreeRecordNode_2<TCellSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, unknown>, RecordNodeInsertableData_2<TCellSchema>, true, TCellSchema, undefined, unknown>> | undefined) & InsertableTypedNode_2<TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, TreeRecordNode_2<TCellSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, unknown>, RecordNodeInsertableData_2<TCellSchema>, true, TCellSchema, undefined, unknown>>;
1505
+ readonly cells: (InsertableTypedNode_2<TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, TreeRecordNode<TCellSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, unknown>, RecordNodeInsertableData_2<TCellSchema>, true, TCellSchema, undefined, unknown>> | undefined) & InsertableTypedNode_2<TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, TreeRecordNode<TCellSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, unknown>, RecordNodeInsertableData_2<TCellSchema>, true, TCellSchema, undefined, unknown>>;
1505
1506
  } & (FieldHasDefault<TPropsSchema> extends true ? {
1506
1507
  props?: InsertableTreeFieldFromImplicitField<TPropsSchema> | undefined;
1507
1508
  } : {
@@ -1509,7 +1510,7 @@ export namespace System_TableSchema {
1509
1510
  }), true, {
1510
1511
  readonly props: TPropsSchema;
1511
1512
  readonly id: FieldSchema_2<FieldKind_2.Identifier, LeafSchema_2<"string", string>, unknown>;
1512
- readonly cells: FieldSchema_2<FieldKind_2.Required, TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, TreeRecordNode_2<TCellSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, unknown>, RecordNodeInsertableData_2<TCellSchema>, true, TCellSchema, undefined, unknown>, unknown>;
1513
+ readonly cells: FieldSchema_2<FieldKind_2.Required, TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, TreeRecordNode<TCellSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, unknown>, RecordNodeInsertableData_2<TCellSchema>, true, TCellSchema, undefined, unknown>, unknown>;
1513
1514
  }>;
1514
1515
  // @system
1515
1516
  export function createTableSchema<const TInputScope extends string | undefined, const TCellSchema extends ImplicitAllowedTypes, const TColumnSchema extends ColumnSchemaBase<TInputScope, TCellSchema>, const TRowSchema extends RowSchemaBase<TInputScope, TCellSchema>>(inputSchemaFactory: SchemaFactoryBeta<TInputScope>, _cellSchema: TCellSchema, columnSchema: TColumnSchema, rowSchema: TRowSchema): TreeNodeSchemaCore_2<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Table">, NodeKind.Object, true, {
@@ -1636,10 +1637,6 @@ export namespace TableSchema {
1636
1637
  }
1637
1638
  // @sealed
1638
1639
  export interface Column<TCell extends ImplicitAllowedTypes, TProps extends ImplicitFieldSchema = ImplicitFieldSchema> {
1639
- getCells(): readonly {
1640
- rowId: string;
1641
- cell: TreeNodeFromImplicitAllowedTypes<TCell>;
1642
- }[];
1643
1640
  readonly id: string;
1644
1641
  get props(): TreeFieldFromImplicitField<TProps>;
1645
1642
  set props(value: InsertableTreeFieldFromImplicitField<TProps>);
@@ -1658,19 +1655,9 @@ export namespace TableSchema {
1658
1655
  }
1659
1656
  // @sealed
1660
1657
  export interface Row<TCell extends ImplicitAllowedTypes, TProps extends ImplicitFieldSchema = ImplicitFieldSchema> {
1661
- getCell(column: Column<TCell>): TreeNodeFromImplicitAllowedTypes<TCell> | undefined;
1662
- getCell(columnId: string): TreeNodeFromImplicitAllowedTypes<TCell> | undefined;
1663
- getCells(): readonly {
1664
- columnId: string;
1665
- cell: TreeNodeFromImplicitAllowedTypes<TCell>;
1666
- }[];
1667
1658
  readonly id: string;
1668
1659
  get props(): TreeFieldFromImplicitField<TProps>;
1669
1660
  set props(value: InsertableTreeFieldFromImplicitField<TProps>);
1670
- removeCell(column: Column<TCell>): TreeNodeFromImplicitAllowedTypes<TCell> | undefined;
1671
- removeCell(columnId: string): TreeNodeFromImplicitAllowedTypes<TCell> | undefined;
1672
- setCell(column: Column<TCell>, value: InsertableTreeNodeFromImplicitAllowedTypes<TCell>): void;
1673
- setCell(columnId: string, value: InsertableTreeNodeFromImplicitAllowedTypes<TCell>): void;
1674
1661
  }
1675
1662
  export function row<const TScope extends string | undefined, const TCell extends ImplicitAllowedTypes>(params: System_TableSchema.CreateRowOptionsBase<SchemaFactoryBeta<TScope>, TCell>): System_TableSchema.RowSchemaBase<TScope, TCell, System_TableSchema.DefaultPropsType>;
1676
1663
  export function row<const TScope extends string | undefined, const TCell extends ImplicitAllowedTypes, const TProps extends ImplicitFieldSchema>(params: System_TableSchema.CreateRowOptionsBase<SchemaFactoryBeta<TScope>, TCell> & {
package/dist/alpha.d.ts CHANGED
@@ -250,7 +250,6 @@ export {
250
250
  RunTransactionParams,
251
251
  SchemaFactoryAlpha,
252
252
  SharedTreeFormatOptions,
253
- SharedTreeFormatVersion,
254
253
  SharedTreeOptions,
255
254
  SimpleAllowedTypeAttributes,
256
255
  SimpleArrayNodeSchema,
@@ -294,6 +293,7 @@ export {
294
293
  allowUnused,
295
294
  asAlpha,
296
295
  asTreeViewAlpha,
296
+ checkCompatibility,
297
297
  cloneWithReplacements,
298
298
  comparePersistedSchema,
299
299
  configuredSharedTree,
@@ -304,11 +304,13 @@ export {
304
304
  decodeSimpleSchema,
305
305
  encodeSimpleSchema,
306
306
  evaluateLazySchema,
307
+ exportCompatibilitySchemaSnapshot,
307
308
  extractPersistedSchema,
308
309
  generateSchemaFromSimpleSchema,
309
310
  getBranch,
310
311
  getJsonSchema,
311
312
  getSimpleSchema,
313
+ importCompatibilitySchemaSnapshot,
312
314
  independentInitializedView,
313
315
  independentView,
314
316
  normalizeAllowedTypes,
package/lib/alpha.d.ts CHANGED
@@ -250,7 +250,6 @@ export {
250
250
  RunTransactionParams,
251
251
  SchemaFactoryAlpha,
252
252
  SharedTreeFormatOptions,
253
- SharedTreeFormatVersion,
254
253
  SharedTreeOptions,
255
254
  SimpleAllowedTypeAttributes,
256
255
  SimpleArrayNodeSchema,
@@ -294,6 +293,7 @@ export {
294
293
  allowUnused,
295
294
  asAlpha,
296
295
  asTreeViewAlpha,
296
+ checkCompatibility,
297
297
  cloneWithReplacements,
298
298
  comparePersistedSchema,
299
299
  configuredSharedTree,
@@ -304,11 +304,13 @@ export {
304
304
  decodeSimpleSchema,
305
305
  encodeSimpleSchema,
306
306
  evaluateLazySchema,
307
+ exportCompatibilitySchemaSnapshot,
307
308
  extractPersistedSchema,
308
309
  generateSchemaFromSimpleSchema,
309
310
  getBranch,
310
311
  getJsonSchema,
311
312
  getSimpleSchema,
313
+ importCompatibilitySchemaSnapshot,
312
314
  independentInitializedView,
313
315
  independentView,
314
316
  normalizeAllowedTypes,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluid-framework",
3
- "version": "2.72.0",
3
+ "version": "2.73.0",
4
4
  "description": "The main entry point into Fluid Framework public packages",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -57,17 +57,17 @@
57
57
  "main": "lib/index.js",
58
58
  "types": "lib/public.d.ts",
59
59
  "dependencies": {
60
- "@fluidframework/container-definitions": "~2.72.0",
61
- "@fluidframework/container-loader": "~2.72.0",
62
- "@fluidframework/core-interfaces": "~2.72.0",
63
- "@fluidframework/core-utils": "~2.72.0",
64
- "@fluidframework/driver-definitions": "~2.72.0",
65
- "@fluidframework/fluid-static": "~2.72.0",
66
- "@fluidframework/map": "~2.72.0",
67
- "@fluidframework/runtime-utils": "~2.72.0",
68
- "@fluidframework/sequence": "~2.72.0",
69
- "@fluidframework/shared-object-base": "~2.72.0",
70
- "@fluidframework/tree": "~2.72.0"
60
+ "@fluidframework/container-definitions": "~2.73.0",
61
+ "@fluidframework/container-loader": "~2.73.0",
62
+ "@fluidframework/core-interfaces": "~2.73.0",
63
+ "@fluidframework/core-utils": "~2.73.0",
64
+ "@fluidframework/driver-definitions": "~2.73.0",
65
+ "@fluidframework/fluid-static": "~2.73.0",
66
+ "@fluidframework/map": "~2.73.0",
67
+ "@fluidframework/runtime-utils": "~2.73.0",
68
+ "@fluidframework/sequence": "~2.73.0",
69
+ "@fluidframework/shared-object-base": "~2.73.0",
70
+ "@fluidframework/tree": "~2.73.0"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@arethetypeswrong/cli": "^0.17.1",
@@ -75,7 +75,7 @@
75
75
  "@fluid-tools/build-cli": "^0.60.0",
76
76
  "@fluidframework/build-common": "^2.0.3",
77
77
  "@fluidframework/build-tools": "^0.60.0",
78
- "@fluidframework/eslint-config-fluid": "~2.72.0",
78
+ "@fluidframework/eslint-config-fluid": "~2.73.0",
79
79
  "@microsoft/api-extractor": "7.52.11",
80
80
  "@types/node": "^18.19.0",
81
81
  "concurrently": "^8.2.1",