From 02dddd9518b3578a63953478d815b0dd4c3c6aa5 Mon Sep 17 00:00:00 2001 From: Alex Currie-Clark Date: Wed, 6 Dec 2023 10:06:02 +0000 Subject: [PATCH 1/2] Adds `import` method to add an existing document to the repo --- packages/automerge-repo/src/Repo.ts | 17 +++++++++++++ packages/automerge-repo/test/Repo.test.ts | 31 ++++++++++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/packages/automerge-repo/src/Repo.ts b/packages/automerge-repo/src/Repo.ts index 275ce3f17..bf25a4f9d 100644 --- a/packages/automerge-repo/src/Repo.ts +++ b/packages/automerge-repo/src/Repo.ts @@ -442,6 +442,23 @@ export class Repo extends EventEmitter { this.emit("delete-document", { documentId }) } + /** + * Imports an existing document into the repo. + */ + import(doc: Automerge.Doc) { + if (!Automerge.isAutomerge(doc)) { + throw new Error("Invalid Automerge document") + } + + const handle = this.create() + + handle.update(() => { + return Automerge.clone(doc) + }) + + return handle + } + subscribeToRemotes = (remotes: StorageId[]) => { this.#log("subscribeToRemotes", { remotes }) this.#remoteHeadsSubscriptions.subscribeToRemotes(remotes) diff --git a/packages/automerge-repo/test/Repo.test.ts b/packages/automerge-repo/test/Repo.test.ts index 129cb6eb4..478cc4761 100644 --- a/packages/automerge-repo/test/Repo.test.ts +++ b/packages/automerge-repo/test/Repo.test.ts @@ -2,7 +2,7 @@ import { next as A } from "@automerge/automerge" import { MessageChannelNetworkAdapter } from "@automerge/automerge-repo-network-messagechannel" import assert from "assert" import * as Uuid from "uuid" -import { describe, it } from "vitest" +import { describe, expect, it } from "vitest" import { READY } from "../src/DocHandle.js" import { parseAutomergeUrl } from "../src/AutomergeUrl.js" import { @@ -396,6 +396,29 @@ describe("Repo", () => { const storageKeyTypes = storageAdapter.keys().map(k => k.split(".")[1]) assert(storageKeyTypes.filter(k => k === "snapshot").length === 1) }) + + it("can import an existing document", async () => { + const { repo } = setup() + const doc = A.init() + const updatedDoc = A.change(doc, d => { + d.foo = "bar" + }) + const handle = repo.import(updatedDoc) + assert.equal(handle.isReady(), true) + const v = await handle.doc() + assert.equal(v?.foo, "bar") + + expect(A.getHistory(v)).toEqual(A.getHistory(updatedDoc)) + }) + + it("throws an error if we try to import an invalid document", async () => { + const { repo } = setup() + try { + repo.import("invalid-doc" as unknown as TestDoc) + } catch (e: any) { + assert.equal(e.message, "Invalid Automerge document") + } + }) }) describe("with peers (linear network)", async () => { @@ -743,9 +766,9 @@ describe("Repo", () => { const doc = Math.random() < 0.5 ? // heads, create a new doc - repo.create() + repo.create() : // tails, pick a random doc - (getRandomItem(docs) as DocHandle) + (getRandomItem(docs) as DocHandle) // make sure the doc is ready if (!doc.isReady()) { @@ -1102,7 +1125,7 @@ describe("Repo", () => { }) const warn = console.warn -const NO_OP = () => {} +const NO_OP = () => { } const disableConsoleWarn = () => { console.warn = NO_OP From 72eec7f63749bfa4cd47a69045937e20f1582945 Mon Sep 17 00:00:00 2001 From: Alex Currie-Clark Date: Tue, 12 Dec 2023 10:09:24 +0000 Subject: [PATCH 2/2] Updated import signature to take a `Uint8Array` instead of a document Added method description to readme --- packages/automerge-repo/README.md | 20 ++++++++++++-------- packages/automerge-repo/src/Repo.ts | 9 ++++----- packages/automerge-repo/test/Repo.test.ts | 19 ++++++++++--------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/packages/automerge-repo/README.md b/packages/automerge-repo/README.md index c1460f025..18b42cf8e 100644 --- a/packages/automerge-repo/README.md +++ b/packages/automerge-repo/README.md @@ -45,6 +45,8 @@ A `Repo` exposes these methods: networks. - `delete(docId: DocumentId)` Deletes the local copy of a document from the local cache and local storage. _This does not currently delete the document from any other peers_. +- `import(binary: Uint8Array)` + Imports a document binary (from `Automerge.save(doc)`) into the repo, returning a new handle - `.on("document", ({handle: DocHandle}) => void)` Registers a callback to be fired each time a new document is loaded or created. - `.on("delete-document", ({handle: DocHandle}) => void)` @@ -64,7 +66,7 @@ the document. A `DocHandle` also emits these events: -- `change({handle: DocHandle, patches: Patch[], patchInfo: PatchInfo})` +- `change({handle: DocHandle, patches: Patch[], patchInfo: PatchInfo})` Called whenever the document changes, the handle's .doc - `delete` Called when the document is deleted locally. @@ -85,11 +87,12 @@ network adapter: const repo = new Repo({ network: [new BroadcastChannelNetworkAdapter()], storage: new IndexedDBStorageAdapter(), - sharePolicy: async (peerId: PeerId, documentId: DocumentId) => true // this is the default + sharePolicy: async (peerId: PeerId, documentId: DocumentId) => true, // this is the default }) ``` ### Share Policy + The share policy is used to determine which document in your repo should be _automatically_ shared with other peers. **The default setting is to share all documents with all peers.** > **Warning** @@ -99,13 +102,13 @@ You can override this by providing a custom share policy. The function should re The share policy will not stop a document being _requested_ by another peer by its `DocumentId`. -```ts +````ts ## Starting the demo app ```bash yarn yarn dev -``` +```` ## Quickstart @@ -272,7 +275,8 @@ you'll need to manually copy the `rootDocId` value between the browsers.) Originally authored by Peter van Hardenberg. With gratitude for contributions by: - - Herb Caudill - - Jeremy Rose - - Alex Currie-Clark - - Dylan Mackenzie + +- Herb Caudill +- Jeremy Rose +- Alex Currie-Clark +- Dylan Mackenzie diff --git a/packages/automerge-repo/src/Repo.ts b/packages/automerge-repo/src/Repo.ts index bf25a4f9d..6122af8f2 100644 --- a/packages/automerge-repo/src/Repo.ts +++ b/packages/automerge-repo/src/Repo.ts @@ -443,12 +443,11 @@ export class Repo extends EventEmitter { } /** - * Imports an existing document into the repo. + * Imports document binary into the repo. + * @param binary - The binary to import */ - import(doc: Automerge.Doc) { - if (!Automerge.isAutomerge(doc)) { - throw new Error("Invalid Automerge document") - } + import(binary: Uint8Array) { + const doc = Automerge.load(binary) const handle = this.create() diff --git a/packages/automerge-repo/test/Repo.test.ts b/packages/automerge-repo/test/Repo.test.ts index 478cc4761..f3aca4987 100644 --- a/packages/automerge-repo/test/Repo.test.ts +++ b/packages/automerge-repo/test/Repo.test.ts @@ -403,7 +403,10 @@ describe("Repo", () => { const updatedDoc = A.change(doc, d => { d.foo = "bar" }) - const handle = repo.import(updatedDoc) + + const saved = A.save(updatedDoc) + + const handle = repo.import(saved) assert.equal(handle.isReady(), true) const v = await handle.doc() assert.equal(v?.foo, "bar") @@ -413,11 +416,9 @@ describe("Repo", () => { it("throws an error if we try to import an invalid document", async () => { const { repo } = setup() - try { - repo.import("invalid-doc" as unknown as TestDoc) - } catch (e: any) { - assert.equal(e.message, "Invalid Automerge document") - } + expect(() => { + repo.import(A.init as unknown as Uint8Array) + }).toThrow() }) }) @@ -766,9 +767,9 @@ describe("Repo", () => { const doc = Math.random() < 0.5 ? // heads, create a new doc - repo.create() + repo.create() : // tails, pick a random doc - (getRandomItem(docs) as DocHandle) + (getRandomItem(docs) as DocHandle) // make sure the doc is ready if (!doc.isReady()) { @@ -1125,7 +1126,7 @@ describe("Repo", () => { }) const warn = console.warn -const NO_OP = () => { } +const NO_OP = () => {} const disableConsoleWarn = () => { console.warn = NO_OP