fluid-framework 2.112.0 → 2.113.1

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,179 @@
1
1
  # fluid-framework
2
2
 
3
+ ## 2.113.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Enable select staged schema upgrades at runtime via view configuration ([#27542](https://github.com/microsoft/FluidFramework/pull/27542)) [44f40e8411](https://github.com/microsoft/FluidFramework/commit/44f40e8411d53bc22939a8f53343863f420bb0de)
8
+
9
+ SharedTree now supports enabling selected staged schema upgrades when initializing or upgrading a document's stored schema.
10
+ This lets applications deploy code that understands a schema change before enabling that change in documents.
11
+ It separates code rollout from feature rollout.
12
+
13
+ #### API
14
+
15
+ Pass `stagedUpgradePolicy` in the configuration object to
16
+ [`ITreeAlpha.viewWith`](https://fluidframework.com/docs/api/tree/viewabletree-interface#viewwith-methodsignature)
17
+ to select which schema upgrades to enable at runtime.
18
+
19
+ Use `StagedSchemaUpgradePolicy.enabledStagedUpgrades(...)` with `SchemaUpgrade` objects from
20
+ [`SchemaFactoryBeta.staged`](https://fluidframework.com/docs/api/tree/schemastaticsbeta-interface#staged-propertysignature)
21
+ or [`SchemaFactoryAlpha.stagedOptional`](https://fluidframework.com/docs/api/tree/schemafactoryalpha-class#stagedoptional-property):
22
+
23
+ The following example defines a staged type, extracts its `SchemaUpgrade` token, and passes it to the view configuration so the staged type is enabled when the schema is upgraded:
24
+
25
+ ```typescript
26
+ const sf = new SchemaFactoryBeta("my-app");
27
+
28
+ class ChecklistItem extends sf.object("ChecklistItem", { text: sf.string }) {}
29
+
30
+ // `staged` wraps the type so it can be enabled at runtime.
31
+ const stagedChecklist = SchemaFactoryBeta.staged(ChecklistItem);
32
+ // The SchemaUpgrade token identifies this staged type.
33
+ const checklistUpgrade = stagedChecklist.metadata.stagedSchemaUpgrade;
34
+
35
+ class AppSchema extends sf.object("AppSchema", {
36
+ items: sf.array([sf.string, stagedChecklist]),
37
+ }) {}
38
+
39
+ const view = tree.viewWith(
40
+ new TreeViewConfigurationAlpha({
41
+ schema: AppSchema,
42
+ stagedUpgradePolicy:
43
+ StagedSchemaUpgradePolicy.enabledStagedUpgrades(checklistUpgrade),
44
+ }),
45
+ );
46
+ ```
47
+
48
+ When `stagedUpgradePolicy` is omitted or `undefined`, the default is
49
+ `StagedSchemaUpgradePolicy.restrictive`.
50
+ This excludes all staged schema upgrades, producing the most conservative stored schema.
51
+
52
+ Advanced callers can provide a custom `StagedSchemaUpgradePolicy` object:
53
+
54
+ ```typescript
55
+ const enabledFeatures = new Set<SchemaUpgrade>([checklistUpgrade]);
56
+
57
+ const view = tree.viewWith(
58
+ new TreeViewConfigurationAlpha({
59
+ schema: AppSchema,
60
+ stagedUpgradePolicy: {
61
+ includeStaged: (upgrade) => enabledFeatures.has(upgrade),
62
+ includeStagedOptional: (upgrade) => enabledFeatures.has(upgrade),
63
+ },
64
+ }),
65
+ );
66
+ ```
67
+
68
+ This is useful for fine-grained rollout control or integration tests.
69
+
70
+ #### Pre-built Policies
71
+
72
+ The `StagedSchemaUpgradePolicy` namespace provides convenient pre-built policies:
73
+ - **`restrictive`** (default): excludes all staged upgrades.
74
+ - **`permissive`**: includes all staged upgrades. Useful in tests.
75
+ - **`enabledStagedUpgrades(...)`**: includes only the specified upgrades.
76
+
77
+ #### Production
78
+
79
+ Applications can use feature flags to control when staged schema upgrades are enabled.
80
+ Previously, enabling a staged schema required a code change that removed the staged wrapper.
81
+ With this API, the staged wrapper stays in code while `stagedUpgradePolicy` decides at runtime which documents enable it.
82
+
83
+ For example, an application adding checklist items can deploy clients that understand the new schema first,
84
+ then enable the stored-schema upgrade only where a feature flag is active:
85
+
86
+ ```typescript
87
+ const sf = new SchemaFactoryBeta("example-app");
88
+
89
+ class ChecklistItem extends sf.object("ChecklistItem", {
90
+ text: sf.string,
91
+ }) {}
92
+
93
+ const stagedChecklistItem = SchemaFactoryBeta.staged(ChecklistItem);
94
+ const checklistItemSchemaUpgrade =
95
+ stagedChecklistItem.metadata.stagedSchemaUpgrade;
96
+
97
+ class AppSchema extends sf.object("AppSchema", {
98
+ // `taskItem` allows plain text today; the staged type is added for future rollout.
99
+ taskItem: sf.optional([sf.string, stagedChecklistItem]),
100
+ }) {}
101
+
102
+ const enableChecklistItems = featureFlags.enableChecklistItems;
103
+
104
+ const view = tree.viewWith(
105
+ new TreeViewConfigurationAlpha({
106
+ schema: AppSchema,
107
+ stagedUpgradePolicy: enableChecklistItems
108
+ ? StagedSchemaUpgradePolicy.enabledStagedUpgrades(
109
+ checklistItemSchemaUpgrade,
110
+ )
111
+ : undefined,
112
+ }),
113
+ );
114
+
115
+ if (view.compatibility.canInitialize) {
116
+ // New documents include the checklist schema only while the rollout is enabled.
117
+ view.initialize(initialContent);
118
+ } else if (view.compatibility.canUpgrade) {
119
+ // Writes the staged type into the stored schema for this document.
120
+ view.upgradeSchema();
121
+ }
122
+ ```
123
+
124
+ Once a staged schema upgrade has been written to a document's stored schema, that change is permanent.
125
+ If `upgradeSchema` is later called from a view that does not include the previously enabled token,
126
+ it throws a `UsageError` because the new target would narrow the stored schema.
127
+
128
+ In practice, keep the upgrade token configured for as long as any document may have been upgraded.
129
+ Once the staged wrapper is removed from the code, the token is no longer needed.
130
+
131
+ #### Testing
132
+
133
+ Tests can verify that the current application version handles documents with staged types enabled.
134
+ Without such testing, it is hard to confirm that staging prepared the application—not just the schema—for the new types.
135
+
136
+ ```typescript
137
+ const currentView = currentAppTree.viewWith(
138
+ new TreeViewConfiguration({ schema: CurrentAppSchema }),
139
+ );
140
+ currentView.initialize(existingTaskDocument);
141
+ await ensureSynchronized();
142
+
143
+ const nextView = asAlpha(nextAppTree).viewWith(
144
+ new TreeViewConfigurationAlpha({
145
+ schema: AppSchemaWithStagedChecklist,
146
+ stagedUpgradePolicy: StagedSchemaUpgradePolicy.enabledStagedUpgrades(
147
+ checklistItemSchemaUpgrade,
148
+ ),
149
+ }),
150
+ );
151
+
152
+ // The next version can read the document, but the checklist shape is not yet
153
+ // in stored schema and cannot be written.
154
+ assert.throws(() =>
155
+ addChecklistItem(nextView.root, { text: "Review rollout" }),
156
+ );
157
+
158
+ nextView.upgradeSchema();
159
+ await ensureSynchronized();
160
+
161
+ // Older clients are now incompatible; the next version can use the staged shape.
162
+ assert.equal(currentView.compatibility.canView, false);
163
+ addChecklistItem(nextView.root, { text: "Review rollout" });
164
+ await validateChecklistScenario(nextView);
165
+ ```
166
+
167
+ - Fix assert when inserting the same node multiple times ([#27734](https://github.com/microsoft/FluidFramework/pull/27734)) [b509d00166](https://github.com/microsoft/FluidFramework/commit/b509d00166773585c42c60e97ec30a86fbd20cd5)
168
+
169
+ When inserting the same node multiple times in a single array insertion, a `UsageError` is now thrown instead of an assert `0xa2b`.
170
+
171
+ For example, this now throws a `UsageError` with message `A "ArrayNodeTest.Item" node was provided more than once in a single insertion. A node may not be in more than one place in the tree.`:
172
+
173
+ ```TypeScript
174
+ array.insertAtEnd(item, item);
175
+ ```
176
+
3
177
  ## 2.112.0
4
178
 
5
179
  ### Minor Changes
@@ -1102,6 +1102,11 @@ export interface ITreeViewConfiguration<TSchema extends ImplicitFieldSchema = Im
1102
1102
  readonly schema: TSchema;
1103
1103
  }
1104
1104
 
1105
+ // @alpha
1106
+ export interface ITreeViewConfigurationAlpha<TSchema extends ImplicitFieldSchema = ImplicitFieldSchema> extends ITreeViewConfiguration<TSchema> {
1107
+ readonly stagedUpgradePolicy?: StagedSchemaUpgradePolicy;
1108
+ }
1109
+
1105
1110
  // @alpha @sealed
1106
1111
  export interface JsonArrayNodeSchema extends JsonNodeSchemaBase<NodeKind.Array, "array"> {
1107
1112
  readonly items: JsonFieldSchema;
@@ -1850,6 +1855,22 @@ export interface SnapshotSchemaCompatibilityOptions {
1850
1855
  readonly versionComparer?: (a: string, b: string) => number;
1851
1856
  }
1852
1857
 
1858
+ // @alpha @input
1859
+ export interface StagedSchemaUpgradePolicy {
1860
+ includeStaged(upgrade: SchemaUpgrade): boolean;
1861
+ includeStagedOptional(upgrade: SchemaUpgrade): boolean;
1862
+ }
1863
+
1864
+ // @alpha
1865
+ export const StagedSchemaUpgradePolicy: StagedSchemaUpgradePolicyFactory;
1866
+
1867
+ // @alpha @sealed
1868
+ export interface StagedSchemaUpgradePolicyFactory {
1869
+ enabledStagedUpgrades(...upgrades: SchemaUpgrade[]): StagedSchemaUpgradePolicy;
1870
+ readonly permissive: StagedSchemaUpgradePolicy;
1871
+ readonly restrictive: StagedSchemaUpgradePolicy;
1872
+ }
1873
+
1853
1874
  // @beta @system
1854
1875
  export namespace System_TableSchema {
1855
1876
  // @sealed @system
@@ -2489,7 +2510,6 @@ export interface TreeViewAlpha<in out TSchema extends ImplicitFieldSchema | Unsa
2489
2510
  readonly events: Listenable<TreeViewEvents & TreeBranchEvents>;
2490
2511
  // (undocumented)
2491
2512
  fork(): ReturnType<TreeBranch["fork"]> & TreeViewAlpha<TSchema>;
2492
- // (undocumented)
2493
2513
  initialize(content: InsertableField<TSchema>): void;
2494
2514
  // (undocumented)
2495
2515
  get root(): ReadableField<TSchema>;
@@ -2516,11 +2536,12 @@ export class TreeViewConfiguration<const TSchema extends ImplicitFieldSchema = I
2516
2536
 
2517
2537
  // @alpha @sealed
2518
2538
  export class TreeViewConfigurationAlpha<const TSchema extends ImplicitFieldSchema = ImplicitFieldSchema> extends TreeViewConfiguration<TSchema> implements TreeSchema {
2519
- constructor(props: ITreeViewConfiguration<TSchema>);
2539
+ constructor(props: ITreeViewConfigurationAlpha<TSchema>);
2520
2540
  // (undocumented)
2521
2541
  readonly definitions: ReadonlyMap<string, SimpleNodeSchema<SchemaType.View> & TreeNodeSchema>;
2522
2542
  // (undocumented)
2523
2543
  readonly root: FieldSchemaAlpha;
2544
+ readonly stagedUpgradePolicy: StagedSchemaUpgradePolicy;
2524
2545
  }
2525
2546
 
2526
2547
  // @public @sealed
package/dist/alpha.d.ts CHANGED
@@ -258,6 +258,7 @@ export {
258
258
  HandleConverter,
259
259
  ICodecOptions,
260
260
  ITreeAlpha,
261
+ ITreeViewConfigurationAlpha,
261
262
  IncrementalEncodingPolicy,
262
263
  IndependentViewOptions,
263
264
  IndependentViewTelemetryOptions,
@@ -326,6 +327,8 @@ export {
326
327
  SimpleObjectNodeSchema,
327
328
  SimpleRecordNodeSchema,
328
329
  SimpleTreeSchema,
330
+ StagedSchemaUpgradePolicy,
331
+ StagedSchemaUpgradePolicyFactory,
329
332
  TextAsTree,
330
333
  TransactionCallbackStatusAlpha,
331
334
  TransactionConstraintAlpha,
package/lib/alpha.d.ts CHANGED
@@ -258,6 +258,7 @@ export {
258
258
  HandleConverter,
259
259
  ICodecOptions,
260
260
  ITreeAlpha,
261
+ ITreeViewConfigurationAlpha,
261
262
  IncrementalEncodingPolicy,
262
263
  IndependentViewOptions,
263
264
  IndependentViewTelemetryOptions,
@@ -326,6 +327,8 @@ export {
326
327
  SimpleObjectNodeSchema,
327
328
  SimpleRecordNodeSchema,
328
329
  SimpleTreeSchema,
330
+ StagedSchemaUpgradePolicy,
331
+ StagedSchemaUpgradePolicyFactory,
329
332
  TextAsTree,
330
333
  TransactionCallbackStatusAlpha,
331
334
  TransactionConstraintAlpha,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluid-framework",
3
- "version": "2.112.0",
3
+ "version": "2.113.1",
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.112.0",
61
- "@fluidframework/container-loader": "~2.112.0",
62
- "@fluidframework/core-interfaces": "~2.112.0",
63
- "@fluidframework/core-utils": "~2.112.0",
64
- "@fluidframework/driver-definitions": "~2.112.0",
65
- "@fluidframework/fluid-static": "~2.112.0",
66
- "@fluidframework/map": "~2.112.0",
67
- "@fluidframework/runtime-utils": "~2.112.0",
68
- "@fluidframework/sequence": "~2.112.0",
69
- "@fluidframework/shared-object-base": "~2.112.0",
70
- "@fluidframework/tree": "~2.112.0"
60
+ "@fluidframework/container-definitions": "~2.113.1",
61
+ "@fluidframework/container-loader": "~2.113.1",
62
+ "@fluidframework/core-interfaces": "~2.113.1",
63
+ "@fluidframework/core-utils": "~2.113.1",
64
+ "@fluidframework/driver-definitions": "~2.113.1",
65
+ "@fluidframework/fluid-static": "~2.113.1",
66
+ "@fluidframework/map": "~2.113.1",
67
+ "@fluidframework/runtime-utils": "~2.113.1",
68
+ "@fluidframework/sequence": "~2.113.1",
69
+ "@fluidframework/shared-object-base": "~2.113.1",
70
+ "@fluidframework/tree": "~2.113.1"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@arethetypeswrong/cli": "^0.18.2",