Automerge R Package Quick Reference
One-page reference for the automerge package. See full
documentation with ?function_name.
Installation
# From R-universe
install.packages("automerge", repos = "https://posit-dev.r-universe.dev")
# From GitHub
pak::pak("posit-dev/automerge-r")Document Lifecycle
library(automerge)
# Create
doc <- am_create() # New document
doc <- am_create("fd3ca50687f13477f1f9ea2b216b958a") # With custom actor ID
# Load/Save
bytes <- am_save(doc) # Save to bytes
doc <- am_load(bytes) # Load from bytes
# Actor ID
actor <- am_get_actor(doc) # Get actor ID (raw bytes)
actor_hex <- am_get_actor_hex(doc) # Get actor ID as hex string
am_set_actor(doc, actor_hex) # Set actor ID (raw, hex, or NULL)
# Fork/Merge
doc2 <- am_fork(doc) # Create independent copy
am_merge(doc, doc2) # Merge doc2 into doc1
# Transactions
am_commit(doc, "message") # Commit changes
am_rollback(doc) # Cancel pending changesBasic Access (Maps)
# S3 operators
doc[["key"]] <- "value" # Set value
value <- doc[["key"]] # Get value
doc$key <- value # Alternative syntax
value <- doc$key # Alternative syntax
# Functional API
am_delete(doc, AM_ROOT, "key") # Delete key
am_put(doc, AM_ROOT, "key", value) # Set value
value <- am_get(doc, AM_ROOT, "key") # Get value
# Introspection
keys <- names(doc) # Get all keys
keys <- am_keys(doc, AM_ROOT) # Functional version
values <- am_values(doc, AM_ROOT) # Get all values
n <- length(doc) # Number of keys
n <- am_length(doc, AM_ROOT) # Functional versionNested Objects
# Automatic recursive conversion (recommended)
am_put(
doc,
AM_ROOT,
"user",
list(
name = "Alice",
age = 30L,
address = list(city = "NYC", zip = 10001L)
)
)
# Path-based access (simple for deep structures)
am_put_path(doc, c("user", "address", "city"), "Boston")
city <- am_get_path(doc, c("user", "address", "city"))
am_delete_path(doc, c("user", "address"))
# Manual access
user_obj <- am_get(doc, AM_ROOT, "user")
am_put(doc, user_obj, "name", "Bob")
name <- am_get(doc, user_obj, "name")Lists
# Create list
am_put(doc, AM_ROOT, "items", AM_OBJ_TYPE_LIST)
items <- am_get(doc, AM_ROOT, "items")
# Operations (1-based indexing)
am_insert(doc, items, 1, "first") # Insert at index 1
am_put(doc, items, 1, "FIRST") # Replace at index 1
am_put(doc, items, "end", "append") # Append to end
value <- am_get(doc, items, 1) # Get index 1
am_delete(doc, items, 1) # Delete index 1
# Introspection
n <- am_length(doc, items) # List lengthText Objects
# Text objects use 0-based inter-character positions
# For the text "Hello":
# H e l l o
# 0 1 2 3 4 5 <- positions (0-based, between characters)
# Create text object
am_put(doc, AM_ROOT, "content", am_text("Hello"))
text_obj <- am_get(doc, AM_ROOT, "content")
# Operations
am_text_splice(text_obj, 5, 0, " World") # Insert at position 5
content <- am_text_content(text_obj) # Get full text
# Update text (ideal for collaborative editing)
old_text <- am_text_content(text_obj)
am_text_update(text_obj, old_text, "Hello Universe") # Computes and applies diff
# Cursors (stable positions)
cursor <- am_cursor(text_obj, 5) # Create cursor at position 5
pos <- am_cursor_position(cursor) # Get current position
# Marks (formatting)
am_mark(text_obj, 0, 5, "bold", TRUE, expand = "none")
marks <- am_marks(text_obj) # Get all marks
marks_at <- am_marks_at(text_obj, 2) # Marks at position 2Value Types
# NULL
am_put(doc, AM_ROOT, "null", NULL)
# Boolean
am_put(doc, AM_ROOT, "bool", TRUE)
# Integer
am_put(doc, AM_ROOT, "int", 42L)
# Double
am_put(doc, AM_ROOT, "float", 3.14)
# String
am_put(doc, AM_ROOT, "str", "text")
# Raw bytes
am_put(doc, AM_ROOT, "bytes", raw(10))
# Timestamp
am_put(doc, AM_ROOT, "time", Sys.time())
# Counter
am_put(doc, AM_ROOT, "score", am_counter(0))
am_counter_increment(doc, AM_ROOT, "score", 10)
value <- am_get(doc, AM_ROOT, "score")
# Unsigned 64-bit integer (for cross-platform interop)
am_put(doc, AM_ROOT, "id", am_uint64(12345))
# Explicit type constructors
am_put(doc, AM_ROOT, "items", am_list()) # Empty list
am_put(doc, AM_ROOT, "config", am_map()) # Empty map
am_put(doc, AM_ROOT, "notes", am_text()) # Text objectSynchronization (Low-Level)
# Create sync state
sync_state <- am_sync_state_new()
# Encode/decode messages
msg <- am_sync_encode(doc, sync_state)
am_sync_decode(doc, sync_state, msg)
# Manual sync loop
repeat {
msg1 <- am_sync_encode(doc1, sync1)
if (is.null(msg1)) {
break
}
am_sync_decode(doc2, sync2, msg1)
msg2 <- am_sync_encode(doc2, sync2)
if (is.null(msg2)) {
break
}
am_sync_decode(doc1, sync1, msg2)
}History & Changes
# Get heads
heads <- am_get_heads(doc)
# Get changes
changes <- am_get_changes(doc, NULL) # All changes
changes <- am_get_changes(doc, heads) # Since specific heads
# Apply changes
am_apply_changes(doc, changes)
# Full history
history <- am_get_history(doc)Conversion
# R → Automerge
doc <- as_automerge(list(name = "Alice", age = 30L))
# Automerge → R
r_list <- from_automerge(doc)
r_list <- as.list(doc) # S3 method for documents
text_str <- as.character(text_obj) # S3 method for text objectsConstants
AM_ROOT # Root object (NULL)
AM_OBJ_TYPE_LIST # "list"
AM_OBJ_TYPE_MAP # "map"
AM_OBJ_TYPE_TEXT # "text"
AM_MARK_EXPAND_NONE # "none" (mark doesn't expand at boundaries)
AM_MARK_EXPAND_BEFORE # "before" (expands when text inserted before start)
AM_MARK_EXPAND_AFTER # "after" (expands when text inserted after end)
AM_MARK_EXPAND_BOTH # "both" (expands at both boundaries)System Requirements
- R >= 4.2
- For building from source:
- Rust >= 1.89.0 (https://rustup.rs/)
- CMake >= 3.25 (included in Rtools43+ on Windows)
- Or: Install automerge-c (with UTF-32 character indexing) system-wide to skip build
Resources
- Package site: https://posit-dev.github.io/automerge-r/
- Automerge docs: https://automerge.org/
- Binary format: https://automerge.org/automerge-binary-format-spec/
- CRDT research: https://crdt.tech/
Note: This is a quick reference. See full vignettes for detailed examples and explanations.