Skip to content

Add a TreeAlpha.key2 method #24623

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

Merged
merged 6 commits into from
May 16, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/tough-parts-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@fluidframework/tree": minor
"__section": feature
---
TreeAlpha.key2 API added

The `TreeAlpha.key2` method is meant to eventually replace the public `Tree.key` method. This new method now returns undefined in the case where there is a root node.
1 change: 1 addition & 0 deletions packages/dds/tree/api-report/tree.alpha.api.md
Original file line number Diff line number Diff line change
@@ -1027,6 +1027,7 @@ export interface TreeAlpha {
} & ICodecOptions): Unhydrated<TreeFieldFromImplicitField<TSchema>>;
importConcise<const TSchema extends ImplicitFieldSchema | UnsafeUnknownSchema>(schema: UnsafeUnknownSchema extends TSchema ? ImplicitFieldSchema : TSchema & ImplicitFieldSchema, data: ConciseTree | undefined): Unhydrated<TSchema extends ImplicitFieldSchema ? TreeFieldFromImplicitField<TSchema> : TreeNode | TreeLeafValue | undefined>;
importVerbose<const TSchema extends ImplicitFieldSchema>(schema: TSchema, data: VerboseTree | undefined, options?: Partial<TreeEncodingOptions>): Unhydrated<TreeFieldFromImplicitField<TSchema>>;
key2(node: TreeNode): string | number | undefined;
}

// @alpha
27 changes: 27 additions & 0 deletions packages/dds/tree/src/shared-tree/treeAlpha.ts
Original file line number Diff line number Diff line change
@@ -36,6 +36,9 @@ import {
extractPersistedSchema,
type TreeBranch,
TreeViewConfigurationAlpha,
getStoredKey,
getPropertyKeyFromStoredKey,
treeNodeApi,
mapTreeFromNodeData,
} from "../simple-tree/index.js";
import type { JsonCompatible } from "../util/index.js";
@@ -207,6 +210,15 @@ export interface TreeAlpha {
compressedData: JsonCompatible<IFluidHandle>,
options: { idCompressor?: IIdCompressor } & ICodecOptions,
): Unhydrated<TreeFieldFromImplicitField<TSchema>>;

/**
* The key of the given node under its parent.
* @remarks
* If `node` is an element in a {@link (TreeArrayNode:interface)}, this returns the index of `node` in the array node (a `number`).
* If `node` is the root node, this returns undefined.
* Otherwise, this returns the key of the field that it is under (a `string`).
*/
key2(node: TreeNode): string | number | undefined;
}

/**
@@ -339,6 +351,21 @@ export const TreeAlpha: TreeAlpha = {
const view = independentInitializedView(config, options, content);
return TreeBeta.clone<TSchema>(view.root);
},

key2(node: TreeNode): string | number | undefined {
// If the parent is undefined, then this node is under the root field,
const parent = treeNodeApi.parent(node);
if (parent === undefined) {
return undefined;
}

// The flex-domain strictly operates in terms of "stored keys".
// To find the associated developer-facing "property key", we need to look up the field associated with
// the stored key from the flex-domain, and get property key its simple-domain counterpart was created with.
const storedKey = getStoredKey(node);
const parentSchema = treeNodeApi.schema(parent);
return getPropertyKeyFromStoredKey(parentSchema, storedKey);
},
};

function exportConcise(
8 changes: 7 additions & 1 deletion packages/dds/tree/src/simple-tree/api/index.ts
Original file line number Diff line number Diff line change
@@ -44,7 +44,13 @@ export {
enumFromStrings,
singletonSchema,
} from "./schemaCreationUtilities.js";
export { treeNodeApi, type TreeNodeApi, tryGetSchema } from "./treeNodeApi.js";
export {
treeNodeApi,
type TreeNodeApi,
tryGetSchema,
getStoredKey,
getPropertyKeyFromStoredKey,
} from "./treeNodeApi.js";
export {
createFromInsertable,
cursorFromInsertable,
4 changes: 2 additions & 2 deletions packages/dds/tree/src/simple-tree/api/treeNodeApi.ts
Original file line number Diff line number Diff line change
@@ -288,7 +288,7 @@ export function tryGetSchema(value: unknown): undefined | TreeNodeSchema {
/**
* Gets the stored key with which the provided node is associated in the parent.
*/
function getStoredKey(node: TreeNode): string | number {
export function getStoredKey(node: TreeNode): string | number {
// Note: the flex domain strictly works with "stored keys", and knows nothing about the developer-facing
// "property keys".
const parentField = getOrCreateInnerNode(node).parentField;
@@ -309,7 +309,7 @@ function getStoredKey(node: TreeNode): string | number {
/**
* Given a node schema, gets the property key corresponding with the provided {@link FieldProps.key | stored key}.
*/
function getPropertyKeyFromStoredKey(
export function getPropertyKeyFromStoredKey(
schema: TreeNodeSchema,
storedKey: string | number,
): string | number {
2 changes: 2 additions & 0 deletions packages/dds/tree/src/simple-tree/index.ts
Original file line number Diff line number Diff line change
@@ -88,6 +88,8 @@ export {
type TreeBranch,
type TreeBranchEvents,
tryGetSchema,
getStoredKey,
getPropertyKeyFromStoredKey,
applySchemaToParserOptions,
cursorFromVerbose,
verboseFromCursor,
22 changes: 22 additions & 0 deletions packages/dds/tree/src/test/simple-tree/api/treeNodeApi.spec.ts
Original file line number Diff line number Diff line change
@@ -218,6 +218,28 @@ describe("treeNodeApi", () => {
// TODO: test Deleted status.
});

it("key2", () => {
class Child extends schema.object("Child", {
x: Point,
y: schema.optional(Point, { key: "stable-y" }),
}) {}
const Root = schema.array(Child);
const config = new TreeViewConfiguration({ schema: Root });
const view = getView(config);
view.initialize([
{ x: {}, y: undefined },
{ x: {}, y: {} },
]);
const { root } = view;
assert.equal(TreeAlpha.key2(root), undefined);
assert.equal(TreeAlpha.key2(root[0]), 0);
assert.equal(TreeAlpha.key2(root[0].x), "x");
assert.equal(TreeAlpha.key2(root[1]), 1);
assert.equal(TreeAlpha.key2(root[1].x), "x");
assert(root[1].y !== undefined);
assert.equal(TreeAlpha.key2(root[1].y), "y");
});

describe("shortID", () => {
it("returns local id when an identifier fieldkind exists.", () => {
const schemaWithIdentifier = schema.object("parent", {
Original file line number Diff line number Diff line change
@@ -1409,6 +1409,7 @@ export interface TreeAlpha {
} & ICodecOptions): Unhydrated<TreeFieldFromImplicitField<TSchema>>;
importConcise<const TSchema extends ImplicitFieldSchema | UnsafeUnknownSchema>(schema: UnsafeUnknownSchema extends TSchema ? ImplicitFieldSchema : TSchema & ImplicitFieldSchema, data: ConciseTree | undefined): Unhydrated<TSchema extends ImplicitFieldSchema ? TreeFieldFromImplicitField<TSchema> : TreeNode | TreeLeafValue | undefined>;
importVerbose<const TSchema extends ImplicitFieldSchema>(schema: TSchema, data: VerboseTree | undefined, options?: Partial<TreeEncodingOptions>): Unhydrated<TreeFieldFromImplicitField<TSchema>>;
key2(node: TreeNode): string | number | undefined;
}

// @alpha
Loading
Oops, something went wrong.