From 5015c44e1bfc889f604e074427e52a8693eb1e81 Mon Sep 17 00:00:00 2001 From: Peter van Hardenberg Date: Tue, 7 Jan 2025 16:56:41 -0800 Subject: [PATCH] the patch is in a messy state but tests are passing --- packages/automerge-repo/src/Repo.ts | 51 ++++++++++++------- .../synchronizer/CollectionSynchronizer.ts | 4 +- packages/automerge-repo/test/Repo.test.ts | 4 +- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/packages/automerge-repo/src/Repo.ts b/packages/automerge-repo/src/Repo.ts index 856dfdf85..c670a5a3d 100644 --- a/packages/automerge-repo/src/Repo.ts +++ b/packages/automerge-repo/src/Repo.ts @@ -38,8 +38,8 @@ import type { PeerId, } from "./types.js" import { abortable, AbortOptions } from "./helpers/abortable.js" -import { FindProgress } from "./FindProgress.js" -import { skip } from "node:test" +import { FindProgress, FindProgressWithMethods } from "./FindProgress.js" +import { pause } from "./helpers/pause.js" function randomPeerId() { return ("peer-" + Math.random().toString(36).slice(4)) as PeerId @@ -418,7 +418,7 @@ export class Repo extends EventEmitter { findWithProgress( id: AnyDocumentId, options: AbortOptions = {} - ): FindProgress { + ): FindProgressWithMethods | FindProgress { const { signal } = options const abortPromise = abortable(signal) const documentId = interpretAsDocumentId(id) @@ -427,15 +427,18 @@ export class Repo extends EventEmitter { if (this.#handleCache[documentId]) { const handle = this.#handleCache[documentId] if (handle.state === UNAVAILABLE) { - return { - state: "failed", + const result = { + state: "unavailable" as const, error: new Error(`Document ${id} is unavailable`), + handle, } + return result } if (handle.state === DELETED) { return { state: "failed", error: new Error(`Document ${id} was deleted`), + handle, } } if (handle.state === READY) { @@ -446,6 +449,8 @@ export class Repo extends EventEmitter { } } + // the generator takes over `this`, so we need an alias to the repo this + // eslint-disable-next-line @typescript-eslint/no-this-alias const that = this async function* progressGenerator(): AsyncGenerator> { try { @@ -469,13 +474,14 @@ export class Repo extends EventEmitter { } that.#registerHandleWithSubsystems(handle) + await Promise.race([ - handle.whenReady([READY, UNAVAILABLE, DELETED]), + handle.whenReady([READY, UNAVAILABLE]), abortPromise, ]) if (handle.state === UNAVAILABLE) { - throw new Error(`Document ${id} is unavailable`) + yield { state: "unavailable", handle } } if (handle.state === DELETED) { throw new Error(`Document ${id} was deleted`) @@ -486,6 +492,7 @@ export class Repo extends EventEmitter { yield { state: "failed", error: error instanceof Error ? error : new Error(String(error)), + handle, } } } @@ -497,11 +504,14 @@ export class Repo extends EventEmitter { return { ...result.value, next } } - const untilReady = async (skipReady: boolean) => { + const untilReady = async (allowableStates: string[]) => { for await (const state of iterator) { - if (skipReady && state.handle.state === "requesting") { + if (allowableStates.includes(state.handle.state)) { return state.handle } + if (state.state === "unavailable") { + throw new Error(`Document ${id} is unavailable`) + } if (state.state === "ready") return state.handle if (state.state === "failed") throw state.error } @@ -517,18 +527,21 @@ export class Repo extends EventEmitter { id: AnyDocumentId, options: RepoFindOptions & AbortOptions = {} ): Promise> { - const { skipReady, signal } = options + const { allowableStates = ["ready"], signal } = options const progress = this.findWithProgress(id, { signal }) - if (progress.state === "ready") { + /*if (allowableStates.includes(progress.state)) { + console.log("returning early") return progress.handle - } + }*/ - if (progress.state === "failed") { - throw progress.error + // @ts-expect-error -- my initial result is a FindProgressWithMethods which has untilReady + if (progress.untilReady) { + this.#registerHandleWithSubsystems(progress.handle) + return progress.untilReady(allowableStates) + } else { + return progress.handle } - - return progress.untilReady(skipReady) } /** @@ -573,12 +586,12 @@ export class Repo extends EventEmitter { options: RepoFindOptions & AbortOptions = {} ): Promise> { const documentId = interpretAsDocumentId(id) - const { skipReady, signal } = options + const { allowableStates, signal } = options return Promise.race([ (async () => { const handle = await this.#loadDocument(documentId) - if (!skipReady) { + if (!allowableStates) { await handle.whenReady([READY, UNAVAILABLE]) if (handle.state === UNAVAILABLE && !signal?.aborted) { throw new Error(`Document ${id} is unavailable`) @@ -781,7 +794,7 @@ export interface RepoEvents { } export interface RepoFindOptions { - skipReady?: boolean + allowableStates?: string[] } export interface DocumentPayload { diff --git a/packages/automerge-repo/src/synchronizer/CollectionSynchronizer.ts b/packages/automerge-repo/src/synchronizer/CollectionSynchronizer.ts index abec3d388..f989d3333 100644 --- a/packages/automerge-repo/src/synchronizer/CollectionSynchronizer.ts +++ b/packages/automerge-repo/src/synchronizer/CollectionSynchronizer.ts @@ -110,7 +110,9 @@ export class CollectionSynchronizer extends Synchronizer { this.#docSetUp[documentId] = true - const handle = await this.repo.find(documentId, { skipReady: true }) + const handle = await this.repo.find(documentId, { + allowableStates: ["ready", "unavailable", "requesting"], + }) const docSynchronizer = this.#fetchDocSynchronizer(handle) docSynchronizer.receiveMessage(message) diff --git a/packages/automerge-repo/test/Repo.test.ts b/packages/automerge-repo/test/Repo.test.ts index ca2722003..94cd95052 100644 --- a/packages/automerge-repo/test/Repo.test.ts +++ b/packages/automerge-repo/test/Repo.test.ts @@ -1471,13 +1471,13 @@ describe("Repo.find() abort behavior", () => { await expect(findPromise).rejects.not.toThrow("unavailable") }) - it("returns handle immediately when skipReady is true, even with abort signal", async () => { + it("returns handle immediately when allow unavailable is true, even with abort signal", async () => { const repo = new Repo() const controller = new AbortController() const url = generateAutomergeUrl() const handle = await repo.find(url, { - skipReady: true, + allowableStates: ["unavailable"], signal: controller.signal, })