Skip to content
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

Minor Docs Facelift #61

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
17 changes: 0 additions & 17 deletions docs/api.md

This file was deleted.

15 changes: 6 additions & 9 deletions docs/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,29 @@ This documentation is mostly focused on the javascript implementation of automer

:::


## Core concepts

Using automerge means storing your data in automerge [documents](#Documents). Documents have a [URL](#Document-URLs)s which you can use to share or request documents with/from other peers using a [repository](#Repositories). Repositories give you [`DocHandle`](#dochandles)s which you use to make changes to the document and listen for changes from other peers.

Automerge as used in javascript applications is actually a composition of two libraries. [`automerge-repo`](https://www.npmjs.com/package/@automerge/automerge-repo) which provides the networking and storage plumbing, and [`automerge`](https://www.npmjs.com/package/@automerge/automerge) which provides the CRDT implementation, a transport agnostic [sync protocol](#sync-protocol), and a compressed [storage format](#storage-format) which `automerge-repo` uses to implement various networking and storage plugins.

### Documents
## Documents

A document is the "unit of change" in automerge. It's like a combination of a JSON object and a git repository. What does that mean?

Like a JSON object an automerge document is a map from strings to values, where the values can themselves be maps, arrays, or simple types like strings or numbers. See the [data model](/docs/documents/) section for more details.

Like a git repository an automerge document has a history made up of commits. Every time you make a change to a document you are adding to the history of the document. The combination of this history and some rules about how to handle conflicts means that any two automerge documents can always be merged. See [merging](/docs/under-the-hood/merge_rules.md) for the gory details.

### Repositories
## Repositories

A repository manages connections to remote peers and access to some kind of local storage. Typically you create a repository at application startup and then inject it into the parts of your application which need it. The repository gives out `DocHandle`s, which allow you to access the current state of a document and make changes to it without thinking about how to store those changes, transmit them to others, or fetch changes from others.

Networking and storage for a repository is pluggable. There are various ready made network transports and storage implementations but it is also easy to build your own.

### DocHandles
## DocHandles

A `DocHandle` is an object returned from the various methods on a repository which create or request a document. The `DocHandle` has methods on it to access the underlying automerge document and to create new changes which are stored locally and transmitted to connected peers.

### Document URLs
## Document URLs

Documents in a repository have a URL. An automerge URL looks like this:

Expand All @@ -45,10 +42,10 @@ automerge:2akvofn6L1o4RMUEMQi7qzwRjKWZ

That is, a string of the form `automerge:<base58>`. This URL can be passed to a repository which will use it to check if the document is in any local storage or available from any connected peers.

### Sync Protocol
## Sync Protocol

Repositories communicate with each other using an efficient sync protocol which is implemented in `automerge`. This protocol is transport agnostic and works on a per-document basis, a lot of the work `automerge-repo` does is handling running this sync protocol for multiple documents over different kinds of network.

### Storage Format
## Storage Format

`automerge` implements a compact binary storage format which makes it feasible to store all the editing history of a document (for example, storing every keystroke in a large text document). `automerge-repo` implements the common logic of figuring out when to compress documents and doing so in a way which is safe for concurrent reads and writes.
8 changes: 4 additions & 4 deletions docs/cookbook/modeling-data.md → docs/cookbook.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
---
sidebar_position: 1
sidebar_position: 999
---

# Modeling Data
# Cookbook

All data in Automerge must be stored in a document. A document can be modeled in a variety of ways, and there are many design patterns that can be used. An application could have many documents, typically identified by a UUID.
## Modeling Data

In this section, we will discuss how to model data within a particular document, including how to version and manage data with Automerge in production scenarios.
All data in Automerge must be stored in a document. A document can be modeled in a variety of ways, and there are many design patterns that can be used. An application could have many documents, typically identified by a UUID.

## How many documents?

Expand Down
4 changes: 0 additions & 4 deletions docs/cookbook/_category_.json

This file was deleted.

124 changes: 124 additions & 0 deletions docs/documents/types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
---
sidebar_position: -1
---

# Types

Automerge documents are quite similar to JSON objects. A document always consists of a root map which is a map from strings to other automerge values, which can themselves be composite types.

The types in automerge are:

* Composite types
* Maps
* [List](lists)
* [Text](./text)
* Scalar (non-composite) types:
* IEEE 754 64 bit floating point numbers
* Unsigned integers
* Signed integers
* Booleans Strings
* Timestamps
* Counters
* Byte arrays

See [below](#javascript-language-mapping) for how these types map to JavaScript types.

## Maps

Maps have string keys and any automerge type as a value. "string" here means a unicode string. The underlying representation in automerge is as UTF-8 byte sequences but they are exposed as utf-16 strings in javascript.

## Lists

A list is an ordered sequence of automerge values. The underlying data structure is an RGA sequence, which means that concurrent insertions and deletions can be merged in a manner which attempts to preserve user intent.

## Text

Text is an implementation of the [peritext](https://www.inkandswitch.com/peritext/) CRDT. This is conceptually similar to a [list](#lists) where each element is a single unicode scalar value representing a single character. In addition to the characters `Text` also supports "marks". Marks are tuples of the form `(start, end, name, value)` which have the following meanings:

* `start` - the index of the beginning of the mark
* `end` - the index of the end of the mark
* `name` - the name of the mark
* `value` - any scalar (as in automerge scalar) value

For example, a bold mark from charaters 1 to 5 might be represented as `(1, 5, "bold", true)`.

Note that the restriction to scalar values for the value of a mark will be lifted in future, although mark values will never be mutable - instead you should always create a new mark when updating a value. For now, if you need complex values in a mark you should serialize the value to a string.

## Timestamps

Timestamps are the integer number of milliseconds since the unix epoch (midnight 1970, UTC).

## Counter

Counters are a simple CRDT which just merges by adding all concurrent operations. They can be incremented and decremented.

## Javascript language mapping

The mapping to javascript is accomplished with the use of proxies. This means that in the javascript library maps appear as `object`s and lists appear as `Array`s. There is only one numeric type in javascript - `number` - so the javascript library guesses a bit. If you insert a javascript `number` for which [`Number.isInteger`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger) returns `true` then the number will be inserted as an integer, otherwise it will be a floating point value.

How `Text` and `String` are represented will depend on whether you are using [the `next` API](/docs/the_js_packages#the-next-api)

Timestamps are represented as javascript `Date`s.

Counters are represented as instances of the `Counter` class.

Putting it all together, here's an example of an automerge document containing all the value types:

```typescript
import * as A from "@automerge/automerge/next"

let doc = A.from({
map: {
key: "value",
nested_map: {key: "value"},
nested_list: [1]
},
list: ["a", "b", "c", {nested: "map"}, ["nested list"]],
// Note we are using the `next` API for text, so text sequences are strings
text: "some text",
// In the `next` API non mergable strings are instances of `RawString`.
// You should generally not need to use these. They are retained for backwards
// compatibility
raw_string: new A.RawString("rawstring"),
integer: 1,
float: 2.3,
boolean: true,
bytes: new Uint8Array([1, 2, 3]),
date: new Date(),
counter: new A.Counter(1),
none: null,
})

doc = A.change(doc, d => {
// Insert 'Hello' at the begnning of the string
A.splice(d, ["text"], 0, 0, "Hello ")
d.counter.increment(20)
d.map.key = "new value"
d.map.nested_map.key = "new nested value"
d.list[0] = "A"
d.list.insertAt(0, "Z")
d.list[4].nested = "MAP"
d.list[5][0] = "NESTED LIST"
})

console.log(doc)

// Prints
// {
// map: {
// key: 'new value',
// nested_map: { key: 'new nested value' },
// nested_list: [ 1 ]
// },
// list: [ 'Z', 'A', 'b', 'c', { nested: 'MAP' }, [ 'NESTED LIST' ] ],
// text: 'Hello world',
// raw_string: RawString { val: 'rawstring' },
// integer: 1,
// float: 2.3,
// boolean: true,
// bytes: Uint8Array(3) [ 1, 2, 3 ],
// date: 2023-09-11T13:35:12.229Z,
// counter: Counter { value: 21 },
// none: null
// }
```
2 changes: 1 addition & 1 deletion docs/documents/values.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
sidebar_position: 2
---

# Simple Values
# Primitives

All JSON primitive datatypes are supported in an Automerge document. In addition, JavaScript [Date objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) are supported.

Expand Down
55 changes: 48 additions & 7 deletions docs/quickstart.md → docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@
sidebar_position: 1
---

# 5-Minute Quick Start
# Getting started

It's easy to build a local-first web application with real-time synchronization using Automerge. In this quickstart, we'll start with the standard `yarn create vite` example Typescript application and use Automerge to turn it into a simple local-first application.
It's easy to build a local-first app with real-time synchronization using Automerge. In this guide, we'll turn a standard `yarn create vite` into a simple local-first app. All the code shown here can be found in [this repo](https://github.com/automerge/automerge-repo-quickstart).

All the code here can be found at the [automerge-repo-quickstart](https://github.com/automerge/automerge-repo-quickstart) repo.

Let's begin.

## Setup
## Installation

First, let's initialize an off-the-shelf React app using Vite as our bundler. We're not going to remind you along the way, but we recommend you initialize a git repo and check in the code at whatever interval feels comfortable.

Expand Down Expand Up @@ -57,6 +53,44 @@ export default defineConfig({

With that out of the way, we're ready to build the application.

# Usage

If you just want to know how to use Automerge in greenfield applications, here's how the library is intended to be used:

Install both the `@automerge/automerge` and `@automerge/automerge-repo` packages. Then install the networking and storage plugins you need (typically `@automerge/automerge-repo-network-*` and `@automerge/automerge-repo-storage-*`) packages. Take a look at the cookbook for examples of different ways of using these.

When you're making changes to an automerge document you should use [the `next` API](#the-next-api)

### Using the next API

There are two ways to use the next API

#### Subpath Exports

If you are either using JavaScript in a modern browser or on node > 16 then you can do the following:

```javascript
import {default as A} from "@automerge/automerge/next"
```

Note that for this to work in typescript you will need to have the following in your `tsconfig.json`

```json
...
"module": "NodeNext",
"moduleResolution": "Node16",
...
```

#### The `{ next }` module

If for whatever reason you can't use `@automerge/automerge/next` then you can do this:

```javascript
import {next as A} from "@automerge/automerge"
```


# Using Automerge

The central concept of Automerge is one of documents. An Automerge document is a JSON-like data structure that is kept synchronized between all communicating peers with the same document ID.
Expand Down Expand Up @@ -271,6 +305,13 @@ const repo = new AutomergeRepo.Repo({
Documents will be stored in `IndexedDB` and methods like `Repo.find` will consult storage when loading. The upshot is that if you had a document locally, it will continue to be available regardless of whether you are connected to any peers.


## React

If you are using React, modify the above by:
1. Choosing React as your framework during the `yarn create vite` setup
2. Installing `@automerge/automerge-repo-react-hooks` in addition to the other packages
3

## More

If you're hungry for more, look in the [Cookbook](/docs/cookbook/modeling-data/) section.
66 changes: 0 additions & 66 deletions docs/hello.md

This file was deleted.

Loading