Skip to content

Commit

Permalink
Merge pull request #323 from automerge/prettier
Browse files Browse the repository at this point in the history
Run repo through prettier
  • Loading branch information
pvh authored Mar 20, 2024
2 parents c05e3e0 + 03bd4fa commit 74a9736
Show file tree
Hide file tree
Showing 32 changed files with 316 additions and 279 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ module.exports = {
"no-use-before-define": OFF,
"@typescript-eslint/no-non-null-assertion": OFF,
"@typescript-eslint/no-explicit-any": OFF,
"@typescript-eslint/no-unused-vars": [ERROR, {"varsIgnorePattern": "^_"}],
"@typescript-eslint/no-unused-vars": [ERROR, { varsIgnorePattern: "^_" }],
},
root: true,
}
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
pnpm-lock.yaml
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@ This is a monorepo containing the following packages:
Likely only useful for experimentation, but allows simple (inefficient) tab-to-tab data
synchronization

Please note that a reference sync-server peer which demonstrates the use of
Please note that a reference sync-server peer which demonstrates the use of
[automerge-repo-network-websocket](/packages/automerge-repo-network-websocket/)
is available at [automerge-repo-sync-server](https://github.com/automerge/automerge-repo-sync-server) (this is different from [sync-server](/examples/sync-server)).
4 changes: 1 addition & 3 deletions examples/react-counter/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { AutomergeUrl } from "@automerge/automerge-repo"
import {
useDocument,
} from "@automerge/automerge-repo-react-hooks"
import { useDocument } from "@automerge/automerge-repo-react-hooks"

interface Doc {
count: number
Expand Down
7 changes: 2 additions & 5 deletions examples/react-todo/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { AutomergeUrl } from "@automerge/automerge-repo"
import {
useDocument,
useRepo,
} from "@automerge/automerge-repo-react-hooks"
import { useDocument, useRepo } from "@automerge/automerge-repo-react-hooks"
import cx from "classnames"
import { useRef, useState } from "react"

import { Todo } from "./Todo.js"
import { ExtendedArray, Filter, State, TodoData } from "./types.js"

export function App({url}: {url: AutomergeUrl}) {
export function App({ url }: { url: AutomergeUrl }) {
const [state, changeState] = useDocument<State>(url)

const newTodoInput = useRef<HTMLInputElement>(null)
Expand Down
2 changes: 1 addition & 1 deletion examples/react-todo/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ window.repo = repo
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<RepoContext.Provider value={repo}>
<React.StrictMode>
<App url={docUrl}/>
<App url={docUrl} />
</React.StrictMode>
</RepoContext.Provider>
)
23 changes: 8 additions & 15 deletions examples/react-use-awareness/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'
import { defineConfig } from "vite"
import react from "@vitejs/plugin-react-swc"
import wasm from "vite-plugin-wasm"
import topLevelAwait from "vite-plugin-top-level-await"

export default defineConfig({
plugins: [
react(),
wasm(),
topLevelAwait()
],
plugins: [react(), wasm(), topLevelAwait()],
worker: {
plugins: () => [
wasm(),
topLevelAwait()
]
}
});
plugins: () => [wasm(), topLevelAwait()],
},
})
52 changes: 25 additions & 27 deletions packages/automerge-repo-network-websocket/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,37 @@ Before sync can continue each peer needs to exchange peer IDs and agree on the
protocol version they are using (although currently there is only one version).
Handshake is the following steps:

* Once a connection is established the initiating peer sends a
- Once a connection is established the initiating peer sends a
[join](#join) message with the `senderId` set to the initiating peers ID and
the `protocolVersion` set to "1"
* The receiving peer waits until it receives a message from the initiating
- The receiving peer waits until it receives a message from the initiating
peer, if the initiating peer receives a message before sending the join message
the initiating peer SHOULD terminate the connection.
* When the receiving peer receives the join message
* if the `protocolVersion` is not "1" the receiving peer sends an
[error](#error) message and terminates the connection
* otherwise
* store the `senderId` as the peer ID of the initiating peer
* emit a "peer-candidate" event with the sender ID as the peer
* respond with a [peer](#peer) message with the `targetId` set to the
initiating peers peer ID, the `senderId` set to the receiving peers
peer ID and the `selectedProtocolVersion` set to "1"
* begin the sync phase
* Once the initiating peer has sent the join message it waits for a peer message
- When the receiving peer receives the join message
- if the `protocolVersion` is not "1" the receiving peer sends an
[error](#error) message and terminates the connection
- otherwise
- store the `senderId` as the peer ID of the initiating peer
- emit a "peer-candidate" event with the sender ID as the peer
- respond with a [peer](#peer) message with the `targetId` set to the
initiating peers peer ID, the `senderId` set to the receiving peers
peer ID and the `selectedProtocolVersion` set to "1"
- begin the sync phase
- Once the initiating peer has sent the join message it waits for a peer message
in response. If it receives any other message type before the join message
the receiving peer should send an [error](#error) message and terminates the
connection
* When the initiating peer receives the peer message
* it stores the `senderId` as the peer ID of the receiving peer.
* it emits a "peer-candidate" event with the sender ID as the peer
* if the `selectedProtocolVersion` is anything other than "1" the initiating
- When the initiating peer receives the peer message
- it stores the `senderId` as the peer ID of the receiving peer.
- it emits a "peer-candidate" event with the sender ID as the peer
- if the `selectedProtocolVersion` is anything other than "1" the initiating
peer sends an [error](#error) message and terminates the connection
* it begins the sync phase
- it begins the sync phase

#### Peer IDs and storage IDs

The peer ID is an ephemeral ID which is assumed to only live for the lifetime of the process which advertises the given ID (e.g. a browser tab). Peers may optionally advertise a storage ID in the `join` and `peer` messages, this is an ID which is assumed to be tied to a persistent storage of some kind (e.g. an IndexedDB in a browser). Many peer IDs can advertise the same storage ID (as in the case of many browser tabs). The use of a storage ID allows other peers to know whether to save and reload sync states for a given peer (if the peer advertises a storage ID, then save and reload the sync state attached to that storage ID).


### Sync Phase

In the sync phase either side may send a [request](#request), [sync](#sync),
Expand All @@ -71,13 +70,13 @@ from the `NetworkAdapter` on receipt.

In some cases peers wish to know about the state of peers who are separated from them by several intermediate peers. For example, a tab running a text editor may wish to show whether the contents of the editor are up to date with respect to a tab running in a browser on another users device. This is achieved by gossiping remote heads across intermediate nodes. The logic for this is the following:

* For a given connection each peer maintains a list of the storage IDs the remote peer is interested in (note this is storage IDs, not peer IDs)
* Any peer can send a [`remote-subscription-changed`](#remote-subscription-changed) message to change the set of storage IDs they want the recipient to watch on the sender's behalf
* Any time a peer receives a sync message it checks:
* Is the sync message from a peer with a storage ID which some other remote peer has registered interest in
* Is the remote peer permitted access to the document which the message pertains to (i.e. either the `sharePolicy` return `true` or the local peer is already syncing the document with the remote)
* The local peer sends a [`remote-heads-changed`](#remote-heads-changed) message to each remote peer who passes these checks
* Additionally, whenever the local peer receives a `remote-heads-changed` message it performs the same checks and additionally checks if the timestamp on the `remote-heads-changed` message is greater than the last timestamp for the same storage ID/document combination and if so it forwards it.
- For a given connection each peer maintains a list of the storage IDs the remote peer is interested in (note this is storage IDs, not peer IDs)
- Any peer can send a [`remote-subscription-changed`](#remote-subscription-changed) message to change the set of storage IDs they want the recipient to watch on the sender's behalf
- Any time a peer receives a sync message it checks:
- Is the sync message from a peer with a storage ID which some other remote peer has registered interest in
- Is the remote peer permitted access to the document which the message pertains to (i.e. either the `sharePolicy` return `true` or the local peer is already syncing the document with the remote)
- The local peer sends a [`remote-heads-changed`](#remote-heads-changed) message to each remote peer who passes these checks
- Additionally, whenever the local peer receives a `remote-heads-changed` message it performs the same checks and additionally checks if the timestamp on the `remote-heads-changed` message is greater than the last timestamp for the same storage ID/document combination and if so it forwards it.

In the `browser <-> sync server <-> browser` text editor example above each browser tab would send a `remote-subscription-changed` message to the sync server adding the other browsers storage ID (presumably communicated out of band) to their subscriptions with the sync server. The sync server will then send `remote-heads-changed` messages to each tab when their heads change.

Expand Down Expand Up @@ -205,7 +204,6 @@ it

Sent when a peer wants to send an ephemeral message to another peer


```cddl
{
type: "ephemeral",
Expand Down
13 changes: 10 additions & 3 deletions packages/automerge-repo-network-websocket/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* A `NetworkAdapter` which connects to a remote host via WebSockets
*
* The websocket protocol requires a server to be listening and a client to
* The websocket protocol requires a server to be listening and a client to
* connect to the server. To that end the {@link NodeWSServerAdapter} does not
* make outbound connections and instead listens on the provided socket for
* make outbound connections and instead listens on the provided socket for
* new connections whilst the {@link BrowserWebSocketClientAdapter} makes an
* outbound connection to the provided socket.
*
Expand All @@ -14,5 +14,12 @@
* */
export { BrowserWebSocketClientAdapter } from "./BrowserWebSocketClientAdapter.js"
export { NodeWSServerAdapter } from "./NodeWSServerAdapter.js"
export type { FromClientMessage, FromServerMessage, JoinMessage, LeaveMessage, ErrorMessage, PeerMessage } from "./messages.js"
export type {
FromClientMessage,
FromServerMessage,
JoinMessage,
LeaveMessage,
ErrorMessage,
PeerMessage,
} from "./messages.js"
export type { ProtocolVersion, ProtocolV1 } from "./protocolVersion.js"
11 changes: 7 additions & 4 deletions packages/automerge-repo-react-hooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,29 @@
These hooks are provided as helpers for using Automerge in your React project.

#### [useBootstrap](./src/useBootstrap.ts)

This hook is used to load a document based on the URL hash, for example `//myapp/#documentId=[document ID]`.
It can also load the document ID from localStorage, or create a new document if none is specified.

#### [useLocalAwareness](./src/useLocalAwareness.ts) & [useRemoteAwareness](./src/useRemoteAwareness.ts)

These hooks implement ephemeral awareness/presence, similar to [Yjs Awareness](https://docs.yjs.dev/getting-started/adding-awareness).
They allow temporary state to be shared, such as cursor positions or peer online/offline status.
They allow temporary state to be shared, such as cursor positions or peer online/offline status.

Ephemeral messages are replicated between peers, but not saved to the Automerge doc, and are used for temporary updates that will be discarded.

#### [useRepo/RepoContext](./src/useRepo.ts)

Use RepoContext to set up react context for an Automerge repo.
Use useRepo to lookup the repo from context.
Most hooks depend on RepoContext being available.

#### [useDocument](./src/useDocument.ts)

Return a document & updater fn, by ID.

#### [useHandle](./src/useHandle.ts)

Return a handle, by ID.

## Example usage
Expand All @@ -46,9 +51,7 @@ const sharedWorker = new SharedWorker(

async function getRepo(): Promise<DocCollection> {
return await Repo({
network: [
new BroadcastChannelNetworkAdapter(),
],
network: [new BroadcastChannelNetworkAdapter()],
sharePolicy: peerId => peerId.includes("shared-worker"),
})
}
Expand Down
18 changes: 12 additions & 6 deletions packages/automerge-repo-react-hooks/src/useDocuments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,18 @@ export const useDocuments = <T>(ids?: DocId[]) => {
newIds.forEach(id => {
const handle = repo.find<T>(id)
// As each document loads, update our map
handle.doc().then(doc => {
updateDocument(id, doc)
addListener(handle)
}).catch(err => {
console.error(`Error loading document ${id} in useDocuments: `, err)
})
handle
.doc()
.then(doc => {
updateDocument(id, doc)
addListener(handle)
})
.catch(err => {
console.error(
`Error loading document ${id} in useDocuments: `,
err
)
})
})

// remove any documents that are no longer in the list
Expand Down
Loading

0 comments on commit 74a9736

Please sign in to comment.