Skip to content

Commit

Permalink
Merge branch 'kid-icarus-add-export'
Browse files Browse the repository at this point in the history
  • Loading branch information
pvh committed Dec 19, 2023
2 parents 056580f + 97e2ed7 commit bdbfa6f
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 225 deletions.
4 changes: 3 additions & 1 deletion packages/automerge-repo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ A `Repo` exposes these methods:
- `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
Imports a document binary (from `export()` or `Automerge.save(doc)`) into the repo, returning a new handle
- `export(docId: DocumentId)`
Exports the document. Returns a Promise containing either the Uint8Array of the document or undefined if the document is currently unavailable. See the [Automerge binary format spec](https://automerge.org/automerge-binary-format-spec/) for more details on the shape of the Uint8Array.
- `.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)`
Expand Down
38 changes: 28 additions & 10 deletions packages/automerge-repo/src/Repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,7 @@ export class Repo extends EventEmitter<RepoEvents> {
}: DocHandleEncodedChangePayload<any>) => {
void storageSubsystem.saveDoc(handle.documentId, doc)
}
handle.on(
"heads-changed",
throttle(saveFn, this.saveDebounceRate)
)
handle.on("heads-changed", throttle(saveFn, this.saveDebounceRate))

if (isNew) {
// this is a new document, immediately save it
Expand Down Expand Up @@ -159,7 +156,7 @@ export class Repo extends EventEmitter<RepoEvents> {

const myPeerMetadata: Promise<PeerMetadata> = new Promise(
// eslint-disable-next-line no-async-promise-executor -- TODO: fix
async (resolve) =>
async resolve =>
resolve({
storageId: await storageSubsystem?.id(),
isEphemeral,
Expand Down Expand Up @@ -311,10 +308,13 @@ export class Repo extends EventEmitter<RepoEvents> {
if (!handler) {
handler = this.#throttledSaveSyncStateHandlers[storageId] = throttle(
({ documentId, syncState }: SyncStateMessage) => {
this.storageSubsystem!.saveSyncState(documentId, storageId, syncState)
.catch(err => {
this.#log("error saving sync state", { err })
})
this.storageSubsystem!.saveSyncState(
documentId,
storageId,
syncState
).catch(err => {
this.#log("error saving sync state", { err })
})
},
this.saveDebounceRate
)
Expand Down Expand Up @@ -465,6 +465,22 @@ export class Repo extends EventEmitter<RepoEvents> {
this.emit("delete-document", { documentId })
}

/**
* Exports a document to a binary format.
* @param id - The url or documentId of the handle to export
*
* @returns Promise<Uint8Array | undefined> - A Promise containing the binary document,
* or undefined if the document is unavailable.
*/
async export(id: AnyDocumentId): Promise<Uint8Array | undefined> {
const documentId = interpretAsDocumentId(id)

const handle = this.#getHandle(documentId, false)
const doc = await handle.doc()
if (!doc) return undefined
return Automerge.save(doc)
}

/**
* Imports document binary into the repo.
* @param binary - The binary to import
Expand All @@ -486,7 +502,9 @@ export class Repo extends EventEmitter<RepoEvents> {
this.#log("subscribeToRemotes", { remotes })
this.#remoteHeadsSubscriptions.subscribeToRemotes(remotes)
} else {
this.#log("WARN: subscribeToRemotes called but remote heads gossiping is not enabled")
this.#log(
"WARN: subscribeToRemotes called but remote heads gossiping is not enabled"
)
}
}

Expand Down
22 changes: 22 additions & 0 deletions packages/automerge-repo/test/Repo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Repo } from "../src/Repo.js"
import { eventPromise } from "../src/helpers/eventPromise.js"
import { pause } from "../src/helpers/pause.js"
import {
AnyDocumentId,
AutomergeUrl,
DocHandle,
DocumentId,
Expand Down Expand Up @@ -321,6 +322,27 @@ describe("Repo", () => {
repo.delete(handle.documentId)
}))

it("exports a document", async () => {
const { repo } = setup()
const handle = repo.create<TestDoc>()
handle.change(d => {
d.foo = "bar"
})
assert.equal(handle.isReady(), true)

const exported = await repo.export(handle.documentId)
const loaded = A.load(exported)
const doc = await handle.doc()
assert.deepEqual(doc, loaded)
})

it("rejects when exporting a document that does not exist", async () => {
const { repo } = setup()
assert.rejects(async () => {
await repo.export("foo" as AnyDocumentId)
})
})

it("storage state doesn't change across reloads when the document hasn't changed", async () => {
const storage = new DummyStorageAdapter()

Expand Down
Loading

0 comments on commit bdbfa6f

Please sign in to comment.