Grove.Node (Grove v0.1.1)

View Source

A node in a Grove tree.

Each node represents an element in a hierarchical structure (e.g., a form field, a document section, an AST node). Nodes are identified by unique IDs and maintain parent-child relationships.

Fields

  • :id - Unique identifier, typically "{replica_id}_{clock}"
  • :type - Atom indicating the node type (e.g., :form, :step, :text, :dropdown)
  • :attrs - Node attributes. Either a plain map for simple use cases, or a Grove.Map.ORMap for schema-based nodes with per-field CRDT types.
  • :parent_id - ID of parent node, or nil for root
  • :parent_timestamp - HLC timestamp of the last move operation (for LWW conflict resolution)
  • :position - Sibling ordering metadata for concurrent-safe reordering (see position/0)
  • :children - Ordered list of child node IDs
  • :deleted? - Tombstone flag for soft deletion
  • :meta - Metadata map (e.g., %{updated_by: "user_123"})

Example (Simple attrs)

%Grove.Node{
  id: "replica_1_42",
  type: :dropdown,
  attrs: %{label: "Country", required: true},
  parent_id: "replica_1_12",
  children: ["replica_1_43", "replica_1_44"],
  deleted?: false,
  meta: %{}
}

Example (Schema-based attrs with ORMap)

# When using Grove.Schema, attrs is an ORMap with per-field CRDTs:
node = MySchema.new_node(:dropdown, "replica_1", %{label: "Country"})
# node.attrs is %Grove.Map.ORMap{...}

Summary

Types

Position metadata for sibling ordering.

t()

Functions

Adds a child ID to the end of the children list.

Marks a node as deleted (tombstone).

Returns true if the node is deleted.

Inserts a child ID at the specified position.

Creates a new node with the given id, type, and options.

Removes a child ID from the children list.

Sets the parent ID for a node (used in move operations).

Sets the position metadata for sibling ordering.

Updates node attributes by merging with the provided map.

Types

position()

@type position() :: %{
  left_origin: String.t() | nil,
  right_origin: String.t() | nil,
  timestamp: Grove.HybridLogicalClock.t()
}

Position metadata for sibling ordering.

Uses Fugue-inspired left/right origins for deterministic concurrent ordering:

  • left_origin - ID of the sibling this node should appear after
  • right_origin - ID of the sibling this node should appear before
  • timestamp - HLC timestamp for tiebreaking concurrent insertions

t()

@type t() :: %Grove.Node{
  attrs: map() | Grove.Map.ORMap.t(),
  children: [String.t()],
  deleted?: boolean(),
  id: String.t(),
  meta: map(),
  parent_id: String.t() | nil,
  parent_timestamp: Grove.HybridLogicalClock.t() | nil,
  position: position() | nil,
  type: atom()
}

Functions

add_child(node, child_id)

@spec add_child(t(), String.t()) :: t()

Adds a child ID to the end of the children list.

delete(node)

@spec delete(t()) :: t()

Marks a node as deleted (tombstone).

deleted?(node)

@spec deleted?(t()) :: boolean()

Returns true if the node is deleted.

insert_child(node, child_id, position)

@spec insert_child(t(), String.t(), non_neg_integer()) :: t()

Inserts a child ID at the specified position.

new(id, type, opts \\ [])

@spec new(String.t(), atom(), keyword()) :: t()

Creates a new node with the given id, type, and options.

Options

  • :attrs - Node attributes (default: %{})
  • :parent_id - Parent node ID (default: nil)
  • :parent_timestamp - HLC timestamp for LWW (default: nil)
  • :position - Sibling ordering metadata (default: nil)
  • :children - List of child IDs (default: [])
  • :meta - Metadata map (default: %{})

Example

Grove.Node.new("node_1", :text, attrs: %{label: "Name"}, parent_id: "form_1")

remove_child(node, child_id)

@spec remove_child(t(), String.t()) :: t()

Removes a child ID from the children list.

set_parent(node, parent_id, timestamp \\ nil)

@spec set_parent(t(), String.t() | nil, Grove.HybridLogicalClock.t() | nil) :: t()

Sets the parent ID for a node (used in move operations).

Optionally accepts a timestamp for LWW conflict resolution.

set_position(node, position)

@spec set_position(t(), position()) :: t()

Sets the position metadata for sibling ordering.

Position metadata enables concurrent-safe reordering of siblings using Fugue-inspired left/right origins.

update_attrs(node, new_attrs)

@spec update_attrs(t(), map()) :: t()

Updates node attributes by merging with the provided map.

For plain map attrs, performs a simple Map.merge/2. For ORMap attrs (schema-based nodes), use your schema's update_field/4 instead.