@fluidframework/tree 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.
Files changed (191) hide show
  1. package/CHANGELOG.md +174 -0
  2. package/api-report/tree.alpha.api.md +23 -2
  3. package/dist/entrypoints/alpha.d.ts +1 -1
  4. package/dist/entrypoints/alpha.d.ts.map +1 -1
  5. package/dist/entrypoints/alpha.js +3 -2
  6. package/dist/entrypoints/alpha.js.map +1 -1
  7. package/dist/feature-libraries/default-schema/defaultFieldKinds.js +1 -1
  8. package/dist/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
  9. package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts +39 -7
  10. package/dist/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
  11. package/dist/feature-libraries/modular-schema/fieldChangeHandler.js +22 -2
  12. package/dist/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
  13. package/dist/feature-libraries/modular-schema/genericFieldKind.js +1 -1
  14. package/dist/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  15. package/dist/feature-libraries/modular-schema/index.d.ts +1 -1
  16. package/dist/feature-libraries/modular-schema/index.d.ts.map +1 -1
  17. package/dist/feature-libraries/modular-schema/index.js +2 -1
  18. package/dist/feature-libraries/modular-schema/index.js.map +1 -1
  19. package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  20. package/dist/feature-libraries/modular-schema/modularChangeFamily.js +8 -1
  21. package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  22. package/dist/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
  23. package/dist/feature-libraries/optional-field/optionalField.js +26 -3
  24. package/dist/feature-libraries/optional-field/optionalField.js.map +1 -1
  25. package/dist/feature-libraries/sequence-field/filterEdits.d.ts +12 -0
  26. package/dist/feature-libraries/sequence-field/filterEdits.d.ts.map +1 -0
  27. package/dist/feature-libraries/sequence-field/filterEdits.js +111 -0
  28. package/dist/feature-libraries/sequence-field/filterEdits.js.map +1 -0
  29. package/dist/feature-libraries/sequence-field/markQueue.d.ts +8 -3
  30. package/dist/feature-libraries/sequence-field/markQueue.d.ts.map +1 -1
  31. package/dist/feature-libraries/sequence-field/markQueue.js +22 -8
  32. package/dist/feature-libraries/sequence-field/markQueue.js.map +1 -1
  33. package/dist/feature-libraries/sequence-field/moveEffectTable.d.ts +1 -1
  34. package/dist/feature-libraries/sequence-field/moveEffectTable.d.ts.map +1 -1
  35. package/dist/feature-libraries/sequence-field/moveEffectTable.js +2 -6
  36. package/dist/feature-libraries/sequence-field/moveEffectTable.js.map +1 -1
  37. package/dist/feature-libraries/sequence-field/sequenceFieldChangeRebaser.d.ts +7 -6
  38. package/dist/feature-libraries/sequence-field/sequenceFieldChangeRebaser.d.ts.map +1 -1
  39. package/dist/feature-libraries/sequence-field/sequenceFieldChangeRebaser.js +2 -4
  40. package/dist/feature-libraries/sequence-field/sequenceFieldChangeRebaser.js.map +1 -1
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +2 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/packageVersion.d.ts +1 -1
  46. package/dist/packageVersion.js +1 -1
  47. package/dist/packageVersion.js.map +1 -1
  48. package/dist/shared-tree/schematizingTreeView.d.ts +8 -0
  49. package/dist/shared-tree/schematizingTreeView.d.ts.map +1 -1
  50. package/dist/shared-tree/schematizingTreeView.js +25 -13
  51. package/dist/shared-tree/schematizingTreeView.js.map +1 -1
  52. package/dist/simple-tree/api/configuration.d.ts +32 -2
  53. package/dist/simple-tree/api/configuration.d.ts.map +1 -1
  54. package/dist/simple-tree/api/configuration.js +3 -1
  55. package/dist/simple-tree/api/configuration.js.map +1 -1
  56. package/dist/simple-tree/api/index.d.ts +1 -1
  57. package/dist/simple-tree/api/index.d.ts.map +1 -1
  58. package/dist/simple-tree/api/index.js.map +1 -1
  59. package/dist/simple-tree/api/schemaCompatibilityTester.d.ts +7 -4
  60. package/dist/simple-tree/api/schemaCompatibilityTester.d.ts.map +1 -1
  61. package/dist/simple-tree/api/schemaCompatibilityTester.js +7 -5
  62. package/dist/simple-tree/api/schemaCompatibilityTester.js.map +1 -1
  63. package/dist/simple-tree/api/tree.d.ts +58 -1
  64. package/dist/simple-tree/api/tree.d.ts.map +1 -1
  65. package/dist/simple-tree/api/tree.js.map +1 -1
  66. package/dist/simple-tree/core/index.d.ts +1 -1
  67. package/dist/simple-tree/core/index.d.ts.map +1 -1
  68. package/dist/simple-tree/core/index.js +2 -1
  69. package/dist/simple-tree/core/index.js.map +1 -1
  70. package/dist/simple-tree/core/toStored.d.ts +72 -6
  71. package/dist/simple-tree/core/toStored.d.ts.map +1 -1
  72. package/dist/simple-tree/core/toStored.js +25 -1
  73. package/dist/simple-tree/core/toStored.js.map +1 -1
  74. package/dist/simple-tree/index.d.ts +3 -3
  75. package/dist/simple-tree/index.d.ts.map +1 -1
  76. package/dist/simple-tree/index.js +4 -5
  77. package/dist/simple-tree/index.js.map +1 -1
  78. package/dist/simple-tree/prepareForInsertion.d.ts.map +1 -1
  79. package/dist/simple-tree/prepareForInsertion.js +9 -0
  80. package/dist/simple-tree/prepareForInsertion.js.map +1 -1
  81. package/dist/simple-tree/toStoredSchema.d.ts +11 -27
  82. package/dist/simple-tree/toStoredSchema.d.ts.map +1 -1
  83. package/dist/simple-tree/toStoredSchema.js +25 -33
  84. package/dist/simple-tree/toStoredSchema.js.map +1 -1
  85. package/docs/main/runtime-schema-upgrade.md +208 -0
  86. package/lib/entrypoints/alpha.d.ts +1 -1
  87. package/lib/entrypoints/alpha.d.ts.map +1 -1
  88. package/lib/entrypoints/alpha.js +1 -1
  89. package/lib/entrypoints/alpha.js.map +1 -1
  90. package/lib/feature-libraries/default-schema/defaultFieldKinds.js +1 -1
  91. package/lib/feature-libraries/default-schema/defaultFieldKinds.js.map +1 -1
  92. package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts +39 -7
  93. package/lib/feature-libraries/modular-schema/fieldChangeHandler.d.ts.map +1 -1
  94. package/lib/feature-libraries/modular-schema/fieldChangeHandler.js +21 -1
  95. package/lib/feature-libraries/modular-schema/fieldChangeHandler.js.map +1 -1
  96. package/lib/feature-libraries/modular-schema/genericFieldKind.js +1 -1
  97. package/lib/feature-libraries/modular-schema/genericFieldKind.js.map +1 -1
  98. package/lib/feature-libraries/modular-schema/index.d.ts +1 -1
  99. package/lib/feature-libraries/modular-schema/index.d.ts.map +1 -1
  100. package/lib/feature-libraries/modular-schema/index.js +1 -1
  101. package/lib/feature-libraries/modular-schema/index.js.map +1 -1
  102. package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  103. package/lib/feature-libraries/modular-schema/modularChangeFamily.js +9 -2
  104. package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  105. package/lib/feature-libraries/optional-field/optionalField.d.ts.map +1 -1
  106. package/lib/feature-libraries/optional-field/optionalField.js +27 -4
  107. package/lib/feature-libraries/optional-field/optionalField.js.map +1 -1
  108. package/lib/feature-libraries/sequence-field/filterEdits.d.ts +12 -0
  109. package/lib/feature-libraries/sequence-field/filterEdits.d.ts.map +1 -0
  110. package/lib/feature-libraries/sequence-field/filterEdits.js +107 -0
  111. package/lib/feature-libraries/sequence-field/filterEdits.js.map +1 -0
  112. package/lib/feature-libraries/sequence-field/markQueue.d.ts +8 -3
  113. package/lib/feature-libraries/sequence-field/markQueue.d.ts.map +1 -1
  114. package/lib/feature-libraries/sequence-field/markQueue.js +22 -9
  115. package/lib/feature-libraries/sequence-field/markQueue.js.map +1 -1
  116. package/lib/feature-libraries/sequence-field/moveEffectTable.d.ts +1 -1
  117. package/lib/feature-libraries/sequence-field/moveEffectTable.d.ts.map +1 -1
  118. package/lib/feature-libraries/sequence-field/moveEffectTable.js +2 -6
  119. package/lib/feature-libraries/sequence-field/moveEffectTable.js.map +1 -1
  120. package/lib/feature-libraries/sequence-field/sequenceFieldChangeRebaser.d.ts +7 -6
  121. package/lib/feature-libraries/sequence-field/sequenceFieldChangeRebaser.d.ts.map +1 -1
  122. package/lib/feature-libraries/sequence-field/sequenceFieldChangeRebaser.js +2 -4
  123. package/lib/feature-libraries/sequence-field/sequenceFieldChangeRebaser.js.map +1 -1
  124. package/lib/index.d.ts +1 -1
  125. package/lib/index.d.ts.map +1 -1
  126. package/lib/index.js +1 -1
  127. package/lib/index.js.map +1 -1
  128. package/lib/packageVersion.d.ts +1 -1
  129. package/lib/packageVersion.js +1 -1
  130. package/lib/packageVersion.js.map +1 -1
  131. package/lib/shared-tree/schematizingTreeView.d.ts +8 -0
  132. package/lib/shared-tree/schematizingTreeView.d.ts.map +1 -1
  133. package/lib/shared-tree/schematizingTreeView.js +26 -14
  134. package/lib/shared-tree/schematizingTreeView.js.map +1 -1
  135. package/lib/simple-tree/api/configuration.d.ts +32 -2
  136. package/lib/simple-tree/api/configuration.d.ts.map +1 -1
  137. package/lib/simple-tree/api/configuration.js +4 -2
  138. package/lib/simple-tree/api/configuration.js.map +1 -1
  139. package/lib/simple-tree/api/index.d.ts +1 -1
  140. package/lib/simple-tree/api/index.d.ts.map +1 -1
  141. package/lib/simple-tree/api/index.js.map +1 -1
  142. package/lib/simple-tree/api/schemaCompatibilityTester.d.ts +7 -4
  143. package/lib/simple-tree/api/schemaCompatibilityTester.d.ts.map +1 -1
  144. package/lib/simple-tree/api/schemaCompatibilityTester.js +7 -5
  145. package/lib/simple-tree/api/schemaCompatibilityTester.js.map +1 -1
  146. package/lib/simple-tree/api/tree.d.ts +58 -1
  147. package/lib/simple-tree/api/tree.d.ts.map +1 -1
  148. package/lib/simple-tree/api/tree.js.map +1 -1
  149. package/lib/simple-tree/core/index.d.ts +1 -1
  150. package/lib/simple-tree/core/index.d.ts.map +1 -1
  151. package/lib/simple-tree/core/index.js +1 -1
  152. package/lib/simple-tree/core/index.js.map +1 -1
  153. package/lib/simple-tree/core/toStored.d.ts +72 -6
  154. package/lib/simple-tree/core/toStored.d.ts.map +1 -1
  155. package/lib/simple-tree/core/toStored.js +24 -0
  156. package/lib/simple-tree/core/toStored.js.map +1 -1
  157. package/lib/simple-tree/index.d.ts +3 -3
  158. package/lib/simple-tree/index.d.ts.map +1 -1
  159. package/lib/simple-tree/index.js +2 -2
  160. package/lib/simple-tree/index.js.map +1 -1
  161. package/lib/simple-tree/prepareForInsertion.d.ts.map +1 -1
  162. package/lib/simple-tree/prepareForInsertion.js +9 -0
  163. package/lib/simple-tree/prepareForInsertion.js.map +1 -1
  164. package/lib/simple-tree/toStoredSchema.d.ts +11 -27
  165. package/lib/simple-tree/toStoredSchema.d.ts.map +1 -1
  166. package/lib/simple-tree/toStoredSchema.js +24 -33
  167. package/lib/simple-tree/toStoredSchema.js.map +1 -1
  168. package/package.json +21 -21
  169. package/src/entrypoints/alpha.ts +3 -0
  170. package/src/feature-libraries/default-schema/defaultFieldKinds.ts +1 -1
  171. package/src/feature-libraries/modular-schema/fieldChangeHandler.ts +51 -7
  172. package/src/feature-libraries/modular-schema/genericFieldKind.ts +1 -1
  173. package/src/feature-libraries/modular-schema/index.ts +4 -2
  174. package/src/feature-libraries/modular-schema/modularChangeFamily.ts +16 -1
  175. package/src/feature-libraries/optional-field/optionalField.ts +43 -3
  176. package/src/feature-libraries/sequence-field/filterEdits.ts +143 -0
  177. package/src/feature-libraries/sequence-field/markQueue.ts +28 -11
  178. package/src/feature-libraries/sequence-field/moveEffectTable.ts +2 -7
  179. package/src/feature-libraries/sequence-field/sequenceFieldChangeRebaser.ts +3 -5
  180. package/src/index.ts +3 -0
  181. package/src/packageVersion.ts +1 -1
  182. package/src/shared-tree/schematizingTreeView.ts +42 -20
  183. package/src/simple-tree/api/configuration.ts +40 -2
  184. package/src/simple-tree/api/index.ts +1 -0
  185. package/src/simple-tree/api/schemaCompatibilityTester.ts +8 -4
  186. package/src/simple-tree/api/tree.ts +58 -1
  187. package/src/simple-tree/core/index.ts +2 -1
  188. package/src/simple-tree/core/toStored.ts +95 -8
  189. package/src/simple-tree/index.ts +3 -2
  190. package/src/simple-tree/prepareForInsertion.ts +12 -0
  191. package/src/simple-tree/toStoredSchema.ts +44 -39
@@ -11,6 +11,7 @@ import type { MakeNominal } from "../../util/index.js";
11
11
  import {
12
12
  type AllowedTypesFullEvaluated,
13
13
  NodeKind,
14
+ StagedSchemaUpgradePolicy,
14
15
  type TreeNodeSchema,
15
16
  } from "../core/index.js";
16
17
  import { type FieldSchemaAlpha, type ImplicitFieldSchema, FieldKind } from "../fieldSchema.js";
@@ -155,6 +156,35 @@ export interface ITreeViewConfiguration<
155
156
  readonly schema: TSchema;
156
157
  }
157
158
 
159
+ /**
160
+ * Property-bag configuration for {@link TreeViewConfigurationAlpha} construction.
161
+ * @alpha
162
+ */
163
+ export interface ITreeViewConfigurationAlpha<
164
+ TSchema extends ImplicitFieldSchema = ImplicitFieldSchema,
165
+ > extends ITreeViewConfiguration<TSchema> {
166
+ /**
167
+ * Policy for generating stored schema from the view schema.
168
+ *
169
+ * @remarks
170
+ * If omitted or `undefined`, defaults to {@link StagedSchemaUpgradePolicyFactory.restrictive}
171
+ * which does not enable any staged schema upgrades.
172
+ *
173
+ * If provided, this policy is used when generating stored schema to include in documents via
174
+ * `initialize` / `upgradeSchema` as well as in {@link snapshotSchemaCompatibility} to validate
175
+ * the compatibility of such documents.
176
+ *
177
+ * @example Enabling specific staged upgrades
178
+ * ```typescript
179
+ * const config = new TreeViewConfigurationAlpha({
180
+ * schema: MySchema,
181
+ * stagedUpgradePolicy: StagedSchemaUpgradePolicy.enabledStagedUpgrades(myUpgrade),
182
+ * });
183
+ * ```
184
+ */
185
+ readonly stagedUpgradePolicy?: StagedSchemaUpgradePolicy;
186
+ }
187
+
158
188
  /**
159
189
  * Configuration for {@link ViewableTree.viewWith}.
160
190
  * @sealed @public
@@ -252,14 +282,22 @@ export class TreeViewConfigurationAlpha<
252
282
  SimpleNodeSchema<SchemaType.View> & TreeNodeSchema
253
283
  >;
254
284
 
255
- public constructor(props: ITreeViewConfiguration<TSchema>) {
285
+ /**
286
+ * {@inheritDoc ITreeViewConfigurationAlpha.stagedUpgradePolicy}
287
+ */
288
+ public readonly stagedUpgradePolicy: StagedSchemaUpgradePolicy;
289
+
290
+ public constructor(props: ITreeViewConfigurationAlpha<TSchema>) {
256
291
  super(props);
257
292
  const treeSchema = createTreeSchema(this.schema);
258
293
  this.root = treeSchema.root;
259
294
  this.definitions = treeSchema.definitions;
260
295
 
296
+ this.stagedUpgradePolicy =
297
+ props.stagedUpgradePolicy ?? StagedSchemaUpgradePolicy.restrictive;
298
+
261
299
  // Eagerly perform these conversions to surface errors sooner.
262
- toInitialSchema(this.root);
300
+ toInitialSchema(this.root, this.stagedUpgradePolicy);
263
301
  transformSimpleSchema(treeSchema, toUnhydratedSchema);
264
302
  }
265
303
  }
@@ -6,6 +6,7 @@
6
6
  export type {
7
7
  ITreeViewConfiguration,
8
8
  ITreeConfigurationOptions,
9
+ ITreeViewConfigurationAlpha,
9
10
  } from "./configuration.js";
10
11
  export {
11
12
  TreeViewConfiguration,
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { TreeStoredSchema } from "../../core/index.js";
7
+ import type { SchemaUpgrade, StagedSchemaUpgradePolicy } from "../core/index.js";
7
8
  import { allowsRepoSuperset, defaultSchemaPolicy } from "../../feature-libraries/index.js";
8
9
  import { toUpgradeSchema } from "../toStoredSchema.js";
9
10
  import type { TreeSchema } from "../treeSchema.js";
@@ -12,19 +13,22 @@ import { getDiscrepanciesInAllowedContent } from "./discrepancies.js";
12
13
  import type { SchemaCompatibilityStatus } from "./tree.js";
13
14
 
14
15
  /**
15
- * Determines the compatibility of a stored document
16
- * (based on its stored schema) with a viewer (based on its view schema).
16
+ * Determines the compatibility of a stored document (based on its stored schema) with a viewer (based on its view schema).
17
17
  *
18
- * @remarks
19
18
  * Adapters can be provided to handle differences between the two schema.
20
19
  * Adapters should only use to types in the `view` SchemaRepository.
21
20
  *
22
21
  * TODO: this API violates the parse don't validate design philosophy.
23
22
  * It should be wrapped with (or replaced by) a parse style API.
23
+ *
24
+ * @param viewSchema - Schema for the view
25
+ * @param stored - The stored schema to check compatibility against
26
+ * @param stagedSchemaUpgrades - Staged schema upgrades enabled for this view, or explicit stored-schema generation options
24
27
  */
25
28
  export function checkSchemaCompatibility(
26
29
  viewSchema: TreeSchema,
27
30
  stored: TreeStoredSchema,
31
+ stagedSchemaUpgrades?: Iterable<SchemaUpgrade> | StagedSchemaUpgradePolicy,
28
32
  ): Omit<SchemaCompatibilityStatus, "canInitialize"> {
29
33
  // The public API surface assumes defaultSchemaPolicy
30
34
  const policy = defaultSchemaPolicy;
@@ -40,7 +44,7 @@ export function checkSchemaCompatibility(
40
44
  break;
41
45
  }
42
46
 
43
- const wouldUpgradeTo = toUpgradeSchema(viewSchema.root);
47
+ const wouldUpgradeTo = toUpgradeSchema(viewSchema.root, stagedSchemaUpgrades);
44
48
 
45
49
  const canUpgrade = allowsRepoSuperset(policy, stored, wouldUpgradeTo);
46
50
 
@@ -444,7 +444,7 @@ export interface TreeView<in out TSchema extends ImplicitFieldSchema> extends ID
444
444
  /**
445
445
  * Description of the current compatibility status between the view schema and stored schema.
446
446
  * @remarks
447
- * {@link TreeViewEvents.schemaChanged} is fired when the compatibility status changes.
447
+ * {@link TreeViewEvents.schemaChanged} is fired when the compatibility status of the document's stored schema changes.
448
448
  * See {@link https://fluidframework.com/docs/data-structures/tree/schema-evolution/ | schema-evolution} for more guidance on how to change schema while maintaining compatibility.
449
449
  * Use {@link snapshotSchemaCompatibility} to write tests to validate that this compatibility behaves as desired across schema changes.
450
450
  */
@@ -459,6 +459,44 @@ export interface TreeView<in out TSchema extends ImplicitFieldSchema> extends ID
459
459
  *
460
460
  * It is an error to call this when {@link SchemaCompatibilityStatus.canUpgrade} is false.
461
461
  * {@link SchemaCompatibilityStatus.canUpgrade} being true does not mean that an upgrade is required, nor that an upgrade will have any effect.
462
+ *
463
+ * When using {@link TreeViewConfigurationAlpha} with a {@link ITreeViewConfigurationAlpha.stagedUpgradePolicy},
464
+ * staged schema upgrades matching the configured policy are included in the target stored schema.
465
+ * Once a staged schema upgrade has been enabled in a document's stored schema, loading that document
466
+ * with a view that does not include equivalent staged members in its construction-time policy will cause
467
+ * `upgradeSchema` to throw a `UsageError` because the requested target would narrow the stored schema.
468
+ *
469
+ * @example Enabling a staged allowed type for documents, selected by a feature flag
470
+ *
471
+ * ```typescript
472
+ * const sf = new SchemaFactoryBeta("my-app");
473
+ *
474
+ * class TaskItem extends sf.object("TaskItem", { title: sf.string }) {}
475
+ * class ChecklistItem extends sf.object("ChecklistItem", { text: sf.string }) {}
476
+ *
477
+ * // `staged` wraps ChecklistItem so it can be enabled at runtime.
478
+ * const stagedChecklist = SchemaFactoryBeta.staged(ChecklistItem);
479
+ * const checklistUpgrade = stagedChecklist.metadata.stagedSchemaUpgrade;
480
+ *
481
+ * class AppSchema extends sf.object("AppSchema", {
482
+ * items: sf.array([TaskItem, stagedChecklist]),
483
+ * }) {}
484
+ *
485
+ * // Feature flag controls whether the upgrade is enabled for this session.
486
+ * const policy = featureFlags.enableChecklist
487
+ * ? StagedSchemaUpgradePolicy.enabledStagedUpgrades(checklistUpgrade)
488
+ * : undefined;
489
+ *
490
+ * const view = tree.viewWith(
491
+ * new TreeViewConfigurationAlpha({ schema: AppSchema, stagedUpgradePolicy: policy }),
492
+ * );
493
+ *
494
+ * if (view.compatibility.canUpgrade) {
495
+ * // Writes the staged type into the document's stored schema.
496
+ * view.upgradeSchema();
497
+ * }
498
+ * ```
499
+ *
462
500
  * @privateRemarks
463
501
  * In the future, more upgrade options could be provided here.
464
502
  * Some options that could be added:
@@ -476,8 +514,12 @@ export interface TreeView<in out TSchema extends ImplicitFieldSchema> extends ID
476
514
  /**
477
515
  * Initialize the tree, setting the stored schema to match this view's schema and setting the tree content.
478
516
  *
517
+ * @remarks
479
518
  * Only valid to call when this view's {@link SchemaCompatibilityStatus.canInitialize} is true.
480
519
  *
520
+ * When using {@link TreeViewConfigurationAlpha} with a {@link ITreeViewConfigurationAlpha.stagedUpgradePolicy},
521
+ * staged schema upgrades matching the configured policy are included in the initial stored schema.
522
+ *
481
523
  * Applications should typically call this function before attaching a `SharedTree`.
482
524
  * @param content - The content to initialize the tree with.
483
525
  */
@@ -585,6 +627,21 @@ export interface TreeViewAlpha<
585
627
 
586
628
  set root(newRoot: InsertableField<TSchema>);
587
629
 
630
+ /**
631
+ * Initialize the tree, setting the stored schema to match this view's schema and setting the tree content.
632
+ *
633
+ * @remarks
634
+ * Only valid to call when this view's {@link SchemaCompatibilityStatus.canInitialize} is true.
635
+ *
636
+ * Enables staged schema upgrades declared by {@link ITreeViewConfigurationAlpha.stagedUpgradePolicy} when generating the initial stored schema.
637
+ * Once a staged schema upgrade has been enabled in a document's stored schema, loading that document
638
+ * with a view that does not include equivalent staged members in its construction-time policy will cause
639
+ * a subsequent `upgradeSchema` call to throw a `UsageError` because the stored schema already contains
640
+ * the upgraded members and the new target would narrow it.
641
+ *
642
+ * Applications should typically call this function before attaching a `SharedTree`.
643
+ * @param content - The content to initialize the tree with.
644
+ */
588
645
  initialize(content: InsertableField<TSchema>): void;
589
646
 
590
647
  readonly events: Listenable<TreeViewEvents & TreeBranchEvents>;
@@ -115,7 +115,8 @@ export {
115
115
  export type { SimpleNodeSchemaBase } from "./simpleNodeSchemaBase.js";
116
116
  export {
117
117
  type StoredSchemaGenerationOptions,
118
- type StoredFromViewSchemaGenerationOptions,
118
+ StagedSchemaUpgradePolicy,
119
+ type StagedSchemaUpgradePolicyFactory,
119
120
  ExpectStored,
120
121
  type SimpleSchemaTransformationOptions,
121
122
  Unchanged,
@@ -6,18 +6,20 @@
6
6
  import type { SchemaUpgrade } from "./allowedTypes.js";
7
7
 
8
8
  /**
9
- * Options for transforming a view simple-schema to a stored simple-schema (See {@link TreeStoredSchema}).
9
+ * Policy controlling which staged schema upgrades are included when generating stored schema from a view schema.
10
+ * @input
11
+ * @alpha
10
12
  */
11
- export interface StoredFromViewSchemaGenerationOptions {
13
+ export interface StagedSchemaUpgradePolicy {
12
14
  /**
13
- * Determines whether to include {@link SchemaStaticsBeta.staged | staged} allowed types in the resulting stored schema.
15
+ * Determines whether to include staged allowed types in the resulting stored schema.
14
16
  * @remarks
15
17
  * Due to caching, the behavior of this function must be pure.
16
18
  */
17
19
  includeStaged(upgrade: SchemaUpgrade): boolean;
18
20
 
19
21
  /**
20
- * Determines whether to treat a {@link SchemaFactoryAlpha.stagedOptional | staged optional} field as optional
22
+ * Determines whether to treat a staged optional field as optional
21
23
  * (rather than required) in the resulting stored schema.
22
24
  * @remarks
23
25
  * Due to caching, the behavior of this function must be pure.
@@ -25,6 +27,93 @@ export interface StoredFromViewSchemaGenerationOptions {
25
27
  includeStagedOptional(upgrade: SchemaUpgrade): boolean;
26
28
  }
27
29
 
30
+ /**
31
+ * Provides factory methods for creating {@link (StagedSchemaUpgradePolicy:interface)} instances.
32
+ *
33
+ * @remarks
34
+ * Use the properties and methods on this object to obtain staged-schema generation options
35
+ * for different scenarios:
36
+ *
37
+ * - {@link StagedSchemaUpgradePolicyFactory.restrictive} — no staged upgrades (default)
38
+ *
39
+ * - {@link StagedSchemaUpgradePolicyFactory.permissive} — all staged upgrades enabled
40
+ *
41
+ * - {@link StagedSchemaUpgradePolicyFactory.enabledStagedUpgrades} — only specific upgrades enabled
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * // Enable specific upgrades:
46
+ * const options = StagedSchemaUpgradePolicy.enabledStagedUpgrades(myUpgrade);
47
+ *
48
+ * // Use restrictive (default, no staged upgrades):
49
+ * const options = StagedSchemaUpgradePolicy.restrictive;
50
+ *
51
+ * // Use permissive (all staged upgrades, useful for testing):
52
+ * const options = StagedSchemaUpgradePolicy.permissive;
53
+ * ```
54
+ *
55
+ * @sealed
56
+ * @alpha
57
+ */
58
+ export interface StagedSchemaUpgradePolicyFactory {
59
+ /**
60
+ * Restrictive policy — excludes all staged schema members.
61
+ *
62
+ * @remarks
63
+ * Use this when you want the most conservative stored schema for compatibility-sensitive
64
+ * scenarios, or when staged schema upgrades should remain disabled.
65
+ *
66
+ * This is the default behavior when no staged upgrades are enabled.
67
+ */
68
+ readonly restrictive: StagedSchemaUpgradePolicy;
69
+ /**
70
+ * Permissive policy — includes all staged schema upgrades.
71
+ *
72
+ * @remarks
73
+ * Use this for testing, validation, and rollout rehearsal scenarios where you want to exercise
74
+ * future document shapes before enabling staged upgrades broadly.
75
+ */
76
+ readonly permissive: StagedSchemaUpgradePolicy;
77
+ /**
78
+ * Creates options that include only the specified staged schema upgrades.
79
+ *
80
+ * @param upgrades - The staged schema upgrades to enable.
81
+ * @returns Options that include only the specified upgrades.
82
+ *
83
+ * @remarks
84
+ * If an empty set of upgrades is passed, the result is equivalent to
85
+ * `StagedSchemaUpgradePolicy.restrictive`.
86
+ */
87
+ enabledStagedUpgrades(...upgrades: SchemaUpgrade[]): StagedSchemaUpgradePolicy;
88
+ }
89
+
90
+ /**
91
+ * {@inheritDoc (StagedSchemaUpgradePolicyFactory:interface)}
92
+ * @alpha
93
+ */
94
+ export const StagedSchemaUpgradePolicy: StagedSchemaUpgradePolicyFactory = {
95
+ restrictive: {
96
+ includeStaged: () => false,
97
+ includeStagedOptional: () => false,
98
+ },
99
+
100
+ permissive: {
101
+ includeStaged: () => true,
102
+ includeStagedOptional: () => true,
103
+ },
104
+
105
+ enabledStagedUpgrades(...upgrades: SchemaUpgrade[]): StagedSchemaUpgradePolicy {
106
+ if (upgrades.length === 0) {
107
+ return StagedSchemaUpgradePolicy.restrictive;
108
+ }
109
+ const enabledUpgradeSet = new Set(upgrades);
110
+ return {
111
+ includeStaged: (upgrade) => enabledUpgradeSet.has(upgrade),
112
+ includeStagedOptional: (upgrade) => enabledUpgradeSet.has(upgrade),
113
+ };
114
+ },
115
+ };
116
+
28
117
  /**
29
118
  * Marker type indicating that the input schema is already a stored schema.
30
119
  */
@@ -42,9 +131,7 @@ export type Unchanged = typeof Unchanged;
42
131
  /**
43
132
  * Subset of {@link SimpleSchemaTransformationOptions} for when the output is a known to be a stored schema.
44
133
  */
45
- export type StoredSchemaGenerationOptions =
46
- | StoredFromViewSchemaGenerationOptions
47
- | ExpectStored;
134
+ export type StoredSchemaGenerationOptions = StagedSchemaUpgradePolicy | ExpectStored;
48
135
 
49
136
  /**
50
137
  * Options for transforming a schema.
@@ -54,6 +141,6 @@ export type StoredSchemaGenerationOptions =
54
141
  * we will need to add a "ToView" option here.
55
142
  */
56
143
  export type SimpleSchemaTransformationOptions =
57
- | StoredFromViewSchemaGenerationOptions
144
+ | StagedSchemaUpgradePolicy
58
145
  | ExpectStored
59
146
  | Unchanged;
@@ -59,6 +59,8 @@ export {
59
59
  type AllowedTypesFullFromMixed,
60
60
  AnnotatedAllowedTypesInternal,
61
61
  type NumberKeys,
62
+ StagedSchemaUpgradePolicy,
63
+ type StagedSchemaUpgradePolicyFactory,
62
64
  ExpectStored,
63
65
  createSchemaUpgrade,
64
66
  } from "./core/index.js";
@@ -72,6 +74,7 @@ export {
72
74
  TreeViewConfiguration,
73
75
  TreeViewConfigurationAlpha,
74
76
  type ITreeViewConfiguration,
77
+ type ITreeViewConfigurationAlpha,
75
78
  type SchemaCompatibilityStatus,
76
79
  type ITreeConfigurationOptions,
77
80
  SchemaFactory,
@@ -317,8 +320,6 @@ export {
317
320
  toUpgradeSchema,
318
321
  toInitialSchema,
319
322
  toUnhydratedSchema,
320
- restrictiveStoredSchemaGenerationOptions,
321
- permissiveStoredSchemaGenerationOptions,
322
323
  } from "./toStoredSchema.js";
323
324
  export {
324
325
  numberSchema,
@@ -155,6 +155,18 @@ function validateAndPrepare(
155
155
  mapTrees: readonly UnhydratedFlexTreeNode[],
156
156
  scheduleHydrationOverride?: HydrationScheduler,
157
157
  ): void {
158
+ // Detecting duplicates here is the lowest level spot which is the same for hydrated and un-hydrated parents,
159
+ // and is also responsible for other validation, so it seem like the best layer to do this check.
160
+ const seenRoots = new Set<UnhydratedFlexTreeNode>();
161
+ for (const node of mapTrees) {
162
+ if (seenRoots.has(node)) {
163
+ throw new UsageError(
164
+ `A ${JSON.stringify(node.type)} node was provided more than once in a single insertion. A node may not be in more than one place in the tree.`,
165
+ );
166
+ }
167
+ seenRoots.add(node);
168
+ }
169
+
158
170
  if (hydratedData !== undefined) {
159
171
  // Run `prepareContentForHydration` before walking the tree in `isFieldInSchema`.
160
172
  // This ensures that when `isFieldInSchema` requests identifiers (or any other contextual defaults),
@@ -37,9 +37,9 @@ import {
37
37
  ExpectStored,
38
38
  NodeKind,
39
39
  SchemaUpgrade,
40
+ StagedSchemaUpgradePolicy,
40
41
  Unchanged,
41
42
  type SimpleSchemaTransformationOptions,
42
- type StoredFromViewSchemaGenerationOptions,
43
43
  type StoredSchemaGenerationOptions,
44
44
  } from "./core/index.js";
45
45
  import { FieldKind, normalizeFieldSchema, type ImplicitFieldSchema } from "./fieldSchema.js";
@@ -64,63 +64,68 @@ import { createTreeSchema } from "./treeSchema.js";
64
64
  // The simple-schema related logic from this file and src/simple-tree/core/toStored.ts can be unified and `toStoredSchema` and its other ImplicitFieldSchema consuming variants should probably be removed in favor of acting on TreeSchema and SimpleTreeSchema directly.
65
65
 
66
66
  const viewToStoredCache = new WeakMap<
67
- StoredFromViewSchemaGenerationOptions,
67
+ StagedSchemaUpgradePolicy,
68
68
  WeakMap<ImplicitFieldSchema, TreeStoredSchema>
69
69
  >();
70
70
 
71
- /**
72
- * Maximally restrictive transformation of a view to stored schema.
73
- * @remarks
74
- * This should only be used when the intent is to produce a stored schema is as restrictive as possible while still being compatible with the input view schema.
75
- * This is typically used for cases where backwards compatibility with past versions of an application is required, like {@link toUpgradeSchema} or {@link toInitialSchema}.
76
- */
77
- export const restrictiveStoredSchemaGenerationOptions: StoredFromViewSchemaGenerationOptions =
78
- {
79
- includeStaged: () => false,
80
- includeStagedOptional: () => false,
81
- };
71
+ function isStagedSchemaUpgradePolicy(
72
+ stagedSchemaUpgrades: Iterable<SchemaUpgrade> | StagedSchemaUpgradePolicy,
73
+ ): stagedSchemaUpgrades is StagedSchemaUpgradePolicy {
74
+ return (
75
+ typeof stagedSchemaUpgrades === "object" &&
76
+ "includeStaged" in stagedSchemaUpgrades &&
77
+ "includeStagedOptional" in stagedSchemaUpgrades
78
+ );
79
+ }
82
80
 
83
81
  /**
84
- * Maximally permissive transformation of a view to stored schema.
82
+ * Resolves a collection of staged schema upgrades into stored-schema generation options.
85
83
  * @remarks
86
- * This should only be used when the intent is to produce a stored schema which allows as much as possible while still being compatible with the input view schema.
87
- * This is typically used for cases where forwards compatibility with future versions of an application is required, like {@link toUnhydratedSchema}.
88
- *
89
- * This is unable to include unknown optional fields in the output, which makes it not truly maximally permissive.
90
- *
91
- * TODO: {@link StoredFromViewSchemaGenerationOptions} could be updated to allow a way to inject extra optional fields.
92
- * If done, then this could take in an existing stored schema, and attempt to generate a valid superset.
93
- * This could be useful to use as the schema for unhydrated content cloned from hydrated content.
84
+ * If `stagedSchemaUpgrades` is omitted, returns restrictive options.
94
85
  */
95
- export const permissiveStoredSchemaGenerationOptions: StoredFromViewSchemaGenerationOptions = {
96
- includeStaged: () => true,
97
- includeStagedOptional: () => true,
98
- };
86
+ export function resolveStoredSchemaGenerationOptions(
87
+ stagedSchemaUpgrades?: Iterable<SchemaUpgrade> | StagedSchemaUpgradePolicy,
88
+ ): StagedSchemaUpgradePolicy {
89
+ if (stagedSchemaUpgrades === undefined) {
90
+ return StagedSchemaUpgradePolicy.restrictive;
91
+ }
92
+
93
+ if (isStagedSchemaUpgradePolicy(stagedSchemaUpgrades)) {
94
+ return stagedSchemaUpgrades;
95
+ }
96
+
97
+ return StagedSchemaUpgradePolicy.enabledStagedUpgrades(...stagedSchemaUpgrades);
98
+ }
99
99
 
100
100
  /**
101
101
  * Converts a {@link ImplicitFieldSchema} into a {@link TreeStoredSchema} for use in schema upgrades.
102
- *
103
- * TODO: once upgrades are more flexible, this should take in more options, including the old schema and specific upgrades to enable.
104
102
  */
105
- export function toUpgradeSchema(root: ImplicitFieldSchema): TreeStoredSchema {
106
- return toStoredSchema(root, restrictiveStoredSchemaGenerationOptions);
103
+ export function toUpgradeSchema(
104
+ root: ImplicitFieldSchema,
105
+ stagedSchemaUpgrades?: Iterable<SchemaUpgrade> | StagedSchemaUpgradePolicy,
106
+ ): TreeStoredSchema {
107
+ return toStoredSchema(root, resolveStoredSchemaGenerationOptions(stagedSchemaUpgrades));
107
108
  }
108
109
 
109
110
  /**
110
111
  * Converts a {@link ImplicitFieldSchema} into a {@link TreeStoredSchema} for use as initial document schema.
111
112
  */
112
- export function toInitialSchema(root: ImplicitFieldSchema): TreeStoredSchema {
113
- return toStoredSchema(root, restrictiveStoredSchemaGenerationOptions);
113
+ export function toInitialSchema(
114
+ root: ImplicitFieldSchema,
115
+ stagedSchemaUpgrades?: Iterable<SchemaUpgrade> | StagedSchemaUpgradePolicy,
116
+ ): TreeStoredSchema {
117
+ return toStoredSchema(root, resolveStoredSchemaGenerationOptions(stagedSchemaUpgrades));
114
118
  }
115
119
 
116
120
  /**
117
- * Converts a {@link ImplicitFieldSchema} into a {@link TreeStoredSchema} to used for unhydrated nodes.
121
+ * Permissive staged schema upgrade policy used for unhydrated node schema generation.
118
122
  * @remarks
119
123
  * This allows as much as possible, relying on further validation when inserting the content.
120
124
  *
121
125
  * TODO: this should get additional options to enable support for unknown optional fields.
122
126
  */
123
- export const toUnhydratedSchema = permissiveStoredSchemaGenerationOptions;
127
+ export const toUnhydratedSchema: StagedSchemaUpgradePolicy =
128
+ StagedSchemaUpgradePolicy.permissive;
124
129
 
125
130
  /**
126
131
  * Converts a {@link ImplicitFieldSchema} into a {@link TreeStoredSchema}.
@@ -137,7 +142,7 @@ export const toUnhydratedSchema = permissiveStoredSchemaGenerationOptions;
137
142
  */
138
143
  export function toStoredSchema(
139
144
  root: ImplicitFieldSchema,
140
- options: StoredFromViewSchemaGenerationOptions,
145
+ options: StagedSchemaUpgradePolicy,
141
146
  ): TreeStoredSchema {
142
147
  const cache = getOrCreate(viewToStoredCache, options, () => new WeakMap());
143
148
  return getOrCreate(cache, root, () => {
@@ -152,7 +157,7 @@ export function toStoredSchema(
152
157
  */
153
158
  export function transformSimpleSchema(
154
159
  schema: SimpleTreeSchema<SchemaType.View>,
155
- options: StoredFromViewSchemaGenerationOptions,
160
+ options: StagedSchemaUpgradePolicy,
156
161
  ): SimpleTreeSchema<SchemaType.Stored>;
157
162
 
158
163
  /**
@@ -324,7 +329,7 @@ export function getStoredSchema(
324
329
  */
325
330
  export function transformSimpleNodeSchema(
326
331
  schema: SimpleNodeSchema<SchemaType.View>,
327
- options: StoredFromViewSchemaGenerationOptions,
332
+ options: StagedSchemaUpgradePolicy,
328
333
  ): SimpleNodeSchema<SchemaType.Stored>;
329
334
 
330
335
  /**
@@ -483,7 +488,7 @@ function allowedTypeFilter(
483
488
  if (options === ExpectStored) {
484
489
  if (data.isStaged !== undefined) {
485
490
  throw new UsageError(
486
- "Failed to covert view schema to stored schema. The simple schema provided was indicated to be a stored schema by the use of `ExpectStored`, but view schema specific content was encountered which requires a `StoredFromViewSchemaGenerationOptions` to process.",
491
+ "Failed to covert view schema to stored schema. The simple schema provided was indicated to be a stored schema by the use of `ExpectStored`, but view schema specific content was encountered which requires a `StagedSchemaUpgradePolicy` to process.",
487
492
  );
488
493
  }
489
494
  return true;
@@ -505,7 +510,7 @@ function allowedTypeFilter(
505
510
 
506
511
  function isStoredFromView(
507
512
  options: SimpleSchemaTransformationOptions,
508
- ): options is StoredFromViewSchemaGenerationOptions {
513
+ ): options is StagedSchemaUpgradePolicy {
509
514
  return typeof options === "object" && "includeStaged" in options;
510
515
  }
511
516