Skip to content

Commit

Permalink
Fix for early subscribers not being updated when a store is ready
Browse files Browse the repository at this point in the history
  • Loading branch information
acurrieclark committed Jan 31, 2024
1 parent 7811a0d commit 568958e
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 14 deletions.
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions src/automerge-svelte-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ export class AutomergeSvelteStore<T>
this.#store = store || null;

this.#state = writable(store?.isReady ? store.doc : null, () => {
if (store?.isReady) {
this.setStore();
}
this.setStore();

return this.#unSubscribe;
});
Expand Down Expand Up @@ -110,7 +108,7 @@ export class AutomergeSvelteStore<T>
private setStore() {
this.#unSubscriber =
this.#store?.subscribe((doc: Doc<T>) => {
this.#state.set(doc);
this.#state.set(doc ?? null);
}) ?? (() => {});
}

Expand Down
47 changes: 47 additions & 0 deletions tests/helpers/dummy-storage-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { StorageAdapter, StorageKey } from "@automerge/automerge-repo";

export class DummyStorageAdapter implements StorageAdapter {
#data: Record<string, Uint8Array> = {};

#keyToString(key: string[]): string {
return key.join(".");
}

#stringToKey(key: string): string[] {
return key.split(".");
}

async loadRange(
keyPrefix: StorageKey,
): Promise<{ data: Uint8Array; key: StorageKey }[]> {
const range = Object.entries(this.#data)
.filter(([key, _]) => key.startsWith(this.#keyToString(keyPrefix)))
.map(([key, data]) => ({ key: this.#stringToKey(key), data }));
return Promise.resolve(range);
}

async removeRange(keyPrefix: string[]): Promise<void> {
Object.entries(this.#data)
.filter(([key, _]) => key.startsWith(this.#keyToString(keyPrefix)))
.forEach(([key, _]) => delete this.#data[key]);
}

async load(key: string[]): Promise<Uint8Array | undefined> {
return new Promise((resolve) =>
resolve(this.#data[this.#keyToString(key)]),
);
}

async save(key: string[], binary: Uint8Array) {
this.#data[this.#keyToString(key)] = binary;
return Promise.resolve();
}

async remove(key: string[]) {
delete this.#data[this.#keyToString(key)];
}

keys() {
return Object.keys(this.#data);
}
}
73 changes: 72 additions & 1 deletion tests/root-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import {
AutomergeStore,
} from "@onsetsoftware/automerge-store";
import { documentData, type DocumentType } from "./data";
import { type Doc, from } from "@automerge/automerge";
import { type Doc, from, load } from "@automerge/automerge";
import { AutomergeSvelteStore } from "../src/automerge-svelte-store";
import { get } from "svelte/store";
import { Repo } from "@automerge/automerge-repo";
import { DummyStorageAdapter } from "./helpers/dummy-storage-adapter";

export const pause = (t = 0) =>
new Promise<void>((resolve) => setTimeout(() => resolve(), t));
Expand Down Expand Up @@ -99,6 +100,76 @@ describe("root store", () => {
});
});

test(
"an automerge repo store updates subscribers who have subscribed before ready",
async () => {
const storage = new DummyStorageAdapter();
const repo = new Repo({
network: [],
storage,
});

const data = {
...documentData,
text: "hello world",
object: {
...documentData.object,
text: "hello world",
},
};

const handle = repo.create<typeof data>();

await handle.doc();

handle.change((doc: Doc<typeof data>) => {
Object.assign(doc, data);
});

await pause(100);

expect(await handle.doc()).toEqual(data);

const connectedRepo = new Repo({
network: [],
storage,
});

const found = connectedRepo.find<DocumentType>(handle.documentId);

const store = new AutomergeRepoStore<DocumentType>(found);
const localRootStore = new AutomergeSvelteStore(store);

expect(get(localRootStore.ready)).toBe(false);
expect(get(localRootStore)).toEqual(null);

const contentsP = new Promise<void>((done) => {
let count = 0;
localRootStore.subscribe((doc) => {
if (count === 0) {
expect(doc).toEqual(null);
count++;
return;
}
expect(doc).toEqual(data);
done();
});
});

const readyP = new Promise<void>((done) => {
localRootStore.ready.subscribe((ready) => {
if (!ready) return;

expect(localRootStore.get()).toEqual(data);
done();
});
});

await Promise.all([contentsP, readyP]);
},
{ timeout: 1000 },
);

test("root store updates when the automerge store updates", () =>
new Promise<void>((done) => {
let initialRun = true;
Expand Down

0 comments on commit 568958e

Please sign in to comment.