Deep References
Sometimes, it is useful to be able to “deep link” to a specific part of a document. For example, if you want to add comments to a text document, it’s useful to maintain those outside of the document: the commenter may not have edit permissions, or you want to prevent different commenters from editing each other’s comments.
Automerge supports this use case with
Refs.
Refs allow you to reference a specific subsection of a document. They can be
obtained through a document handle:
const handle = repo.create({ todos: [ { title: "First", done: false }, { title: "Second", done: true }, ]})const doneRef = handle.ref("todos", 0, "done")// Logs falseconsole.log("first todo done:", doneRef.value())Refs allow you to write to the document as well. When the ref points to a primitive value, just return the new value:
doneRef.change(() => true)// Logs trueconsole.log("first todo done:", doneRef.value())When it points to an object, you can return a brand new object, or you can mutate the object itself:
const firstTodoRef = handle.ref("todos", 0)doneRef.change((value) => value.done = true)// Logs { title: "First", done: true }console.log("first todo:", firstTodoRef.value())If the change function returns undefined, no change is made. This allows
changes to be made conditionally.
Refs can use cursors to reference a specific subsection of a string:
handle = repo.create({ message: "Hello world" })const rangeRef = handle.ref("message", cursor(0, 5))// Logs 'Hello'console.log(rangeRef.value())rangeRef.change(() => "Hi")// The text is replaced at the range; logs "Hi world"console.log(handle.doc().message)Refs can remove the properties they point to. This works for object fields, substrings referenced by cursors, and array elements:
handle = repo.create({ user: { name: "Alice", age: 30, hobbies: ["biking", "spelunking", "weaving"] }})const ageRef = handle.ref("user", "age")ageRef.remove()// Logs undefinedconsole.log("age is", handle.doc().user.age)const nameRef = handle.ref("user", "name", cursor(2,5))nameRef.remove()// Logs Alconsole.log("name is", handle.doc().user.name)const bikingHobbyRef = handle.ref("user", "hobbies", 0)bikingHobbyRef.remove()// logs ['spelunking', 'weaving']console.log("hobbies are ", handle.doc().user.hobbies)Refs don’t need to define explicit paths. They can select document subsections by simple pattern matching:
handle = repo.create({ users: [ { id: "a", name: "Alice" }, { id: "b", name: "Bob" }, { id: "c", name: "Charlie" }, ]})const bobRef = handle.ref("users", { id: "b" })bobRef.remove()// Logs the objects for Alice and Charlieconsole.log(handle.doc().users)The url property on a ref returns a serialized representation of that ref:
this value can be used to create a new ref pointing to the same subsection of
that document. These URLs can be shared like regular Automerge URLs. The
findRef
function can be used to resolve the URL to a Ref object.
The Refs API is currently experimental and may change in a future release.