-
Notifications
You must be signed in to change notification settings - Fork 565
Add tree context API #26432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add tree context API #26432
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| --- | ||
| "@fluidframework/tree": minor | ||
| "__section": tree | ||
| --- | ||
| Added `TreeAlpha.context(node)` to provide context-aware APIs for any SharedTree node, plus a new `TreeContextAlpha` surface for transactions and branch checks. | ||
|
|
||
| This release introduces a node-scoped context that works for nodes inserted into the tree as well as new nodes that are not yet inserted. | ||
| The new `TreeContextAlpha` interface exposes `runTransaction` / `runTransactionAsync` and an `isBranch()` type guard. | ||
| `TreeBranchAlpha` now extends `TreeContextAlpha`, so you can keep using branch APIs when available. | ||
|
|
||
| ### Migration | ||
| If you previously used `TreeAlpha.branch(node)` to discover a branch, switch to `TreeAlpha.context(node)` and check `isBranch()`: | ||
|
|
||
| ```ts | ||
| import { TreeAlpha } from "@fluidframework/tree/alpha"; | ||
|
|
||
| const context = TreeAlpha.context(node); | ||
| if (context.isBranch()) { | ||
| // Same branch APIs as before | ||
| context.fork(); | ||
| } | ||
| ``` | ||
|
|
||
| `TreeAlpha.branch(node)` is now deprecated. | ||
| Prefer the context API above. | ||
|
|
||
| ### New transaction entry point | ||
| You can now run transactions from a node context, regardless of whether the node is hydrated: | ||
|
|
||
| ```ts | ||
| const context = TreeAlpha.context(node); | ||
|
|
||
| // No return value | ||
| const result = context.runTransaction(() => { | ||
| node.count += 1; | ||
| }); | ||
|
|
||
| // Return a value by wrapping it in `{ value }` | ||
| const resultWithValue = context.runTransaction(() => ({ value: node.count })); | ||
| ``` | ||
|
|
||
| For asynchronous work: | ||
|
|
||
| ```ts | ||
| const result = await context.runTransactionAsync(async () => { | ||
| await doWork(); | ||
| return { value: node.count }; | ||
| }); | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -80,9 +80,13 @@ import { | |
| exportConcise, | ||
| borrowCursorFromTreeNodeOrValue, | ||
| contentSchemaSymbol, | ||
| type TreeContextAlpha, | ||
| type TreeNodeSchema, | ||
| getUnhydratedContext, | ||
| type TreeBranchAlpha, | ||
| type TransactionResult, | ||
| type TransactionResultExt, | ||
| type WithValue, | ||
| } from "../simple-tree/index.js"; | ||
| import { brand, extractFromOpaque, type JsonCompatible } from "../util/index.js"; | ||
|
|
||
|
|
@@ -237,9 +241,17 @@ export interface TreeAlpha { | |
| * | ||
| * This does not fork a new branch, but rather retrieves the _existing_ branch for the node. | ||
| * To create a new branch, use e.g. {@link TreeBranch.fork | `myBranch.fork()`}. | ||
| * | ||
| * @deprecated To obtain a {@link TreeBranchAlpha | branch }, use `TreeAlpha.context(node)` to obtain a {@link TreeContextAlpha | context} and then check {@link TreeContextAlpha.isBranch | isBranch()}. | ||
| */ | ||
| branch(node: TreeNode): TreeBranchAlpha | undefined; | ||
|
|
||
| /** | ||
| * Retrieve the {@link TreeContextAlpha | context} for the given node. | ||
| * @param node - The node to query | ||
| */ | ||
| context(node: TreeNode): TreeContextAlpha; | ||
|
|
||
| /** | ||
| * Construct tree content that is compatible with the field defined by the provided `schema`. | ||
| * @param schema - The schema for what to construct. As this is an {@link ImplicitFieldSchema}, a {@link FieldSchema}, {@link TreeNodeSchema} or {@link AllowedTypes} array can be provided. | ||
|
|
@@ -763,6 +775,10 @@ export const TreeAlpha: TreeAlpha = { | |
| return result; | ||
| }, | ||
|
|
||
| context(node: TreeNode): TreeContextAlpha { | ||
| return this.branch(node) ?? UnhydratedTreeContext.instance; | ||
| }, | ||
|
|
||
| branch(node: TreeNode): TreeBranchAlpha | undefined { | ||
| const kernel = getKernel(node); | ||
| if (!kernel.isHydrated()) { | ||
|
|
@@ -1079,3 +1095,41 @@ function borrowFieldCursorFromTreeNodeOrValue( | |
| const mapTree = mapTreeFromCursor(cursor); | ||
| return cursorForMapTreeField([mapTree]); | ||
| } | ||
|
|
||
| class UnhydratedTreeContext implements TreeContextAlpha { | ||
| public static instance = new UnhydratedTreeContext(); | ||
| private constructor() {} | ||
|
|
||
| public isBranch(): this is TreeBranchAlpha { | ||
| return false; | ||
| } | ||
|
|
||
| public runTransaction<TValue>( | ||
| t: () => WithValue<TValue>, | ||
| ): TransactionResultExt<TValue, TValue>; | ||
| public runTransaction(t: () => void): TransactionResult; | ||
| public runTransaction( | ||
| t: () => WithValue<unknown> | void, | ||
| ): TransactionResultExt<unknown, unknown> | TransactionResult { | ||
| return UnhydratedTreeContext.wrapTransactionResult(t()); | ||
| } | ||
|
|
||
| public runTransactionAsync<TValue>( | ||
| t: () => Promise<WithValue<TValue>>, | ||
| ): Promise<TransactionResultExt<TValue, TValue>>; | ||
| public runTransactionAsync(t: () => Promise<void>): Promise<TransactionResult>; | ||
| public async runTransactionAsync( | ||
| t: () => Promise<WithValue<unknown> | void>, | ||
| ): Promise<TransactionResultExt<unknown, unknown> | TransactionResult> { | ||
| return UnhydratedTreeContext.wrapTransactionResult(await t()); | ||
| } | ||
|
|
||
| private static wrapTransactionResult<TValue>( | ||
| value: WithValue<TValue> | void, | ||
| ): TransactionResultExt<TValue, TValue> | TransactionResult { | ||
| if (value?.value !== undefined) { | ||
|
||
| return { success: true, value: value.value }; | ||
| } | ||
| return { success: true }; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.