@automerge/automerge-repo 1.1.3 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"DocHandle.d.ts","sourceRoot":"","sources":["../src/DocHandle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DocHandle.d.ts","sourceRoot":"","sources":["../src/DocHandle.ts"],"names":[],"mappings":"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"}
package/dist/DocHandle.js CHANGED
@@ -201,8 +201,10 @@ export class DocHandle//
201
201
  }
202
202
  /** Returns a promise that resolves when the docHandle is in one of the given states */
203
203
  #statePromise(awaitStates) {
204
- const awaitStatesArray = Array.isArray(awaitStates) ? awaitStates : [awaitStates];
205
- return waitFor(this.#machine, s => awaitStatesArray.some((state) => s.matches(state)),
204
+ const awaitStatesArray = Array.isArray(awaitStates)
205
+ ? awaitStates
206
+ : [awaitStates];
207
+ return waitFor(this.#machine, s => awaitStatesArray.some(state => s.matches(state)),
206
208
  // use a longer delay here so as not to race with other delays
207
209
  { timeout: this.#timeoutDelay * 2 });
208
210
  }
package/dist/Repo.d.ts CHANGED
@@ -82,6 +82,14 @@ export declare class Repo extends EventEmitter<RepoEvents> {
82
82
  import<T>(binary: Uint8Array): DocHandle<T>;
83
83
  subscribeToRemotes: (remotes: StorageId[]) => void;
84
84
  storageId: () => Promise<StorageId | undefined>;
85
+ /**
86
+ * Waits for Repo to finish write changes to disk.
87
+ * @hidden this API is experimental and may change
88
+ * @param documents - if provided, only waits for the specified documents
89
+ * @param timeout - if provided, the maximum time to wait in milliseconds (rejects on timeout)
90
+ * @returns Promise<void>
91
+ */
92
+ flush(documents?: DocumentId[], timeout?: number): Promise<void>;
85
93
  }
86
94
  export interface RepoConfig {
87
95
  /** Our unique identifier */
@@ -1 +1 @@
1
- {"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"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"}
package/dist/Repo.js CHANGED
@@ -383,4 +383,28 @@ export class Repo extends EventEmitter {
383
383
  return this.storageSubsystem.id();
384
384
  }
385
385
  };
386
+ /**
387
+ * Waits for Repo to finish write changes to disk.
388
+ * @hidden this API is experimental and may change
389
+ * @param documents - if provided, only waits for the specified documents
390
+ * @param timeout - if provided, the maximum time to wait in milliseconds (rejects on timeout)
391
+ * @returns Promise<void>
392
+ */
393
+ async flush(documents, timeout) {
394
+ if (!this.storageSubsystem) {
395
+ return Promise.resolve();
396
+ }
397
+ const handles = documents
398
+ ? documents.map(id => this.#handleCache[id])
399
+ : Object.values(this.#handleCache);
400
+ return Promise.all(handles.map(async (handle) => {
401
+ const doc = handle.docSync();
402
+ if (!doc) {
403
+ return;
404
+ }
405
+ return this.storageSubsystem.flush(handle.documentId, doc, timeout);
406
+ })).then(() => {
407
+ /* No-op. To return `voi`d and not `void[]` */
408
+ });
409
+ }
386
410
  }
@@ -11,7 +11,7 @@ import type { NetworkAdapterInterface } from "../../network/NetworkAdapterInterf
11
11
  * - `teardown`: An optional function that will be called after the tests have run. This can be used
12
12
  * to clean up any resources that were created during the test.
13
13
  */
14
- export declare function runAdapterTests(_setup: SetupFn, title?: string): void;
14
+ export declare function runNetworkAdapterTests(_setup: SetupFn, title?: string): void;
15
15
  type Network = NetworkAdapterInterface | NetworkAdapterInterface[];
16
16
  export type SetupFn = () => Promise<{
17
17
  adapters: [Network, Network, Network];
@@ -1 +1 @@
1
- {"version":3,"file":"network-adapter-tests.d.ts","sourceRoot":"","sources":["../../../src/helpers/tests/network-adapter-tests.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAA;AAIvF;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAuJrE;AAID,KAAK,OAAO,GAAG,uBAAuB,GAAG,uBAAuB,EAAE,CAAA;AAElE,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC;IAClC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACrC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB,CAAC,CAAA"}
1
+ {"version":3,"file":"network-adapter-tests.d.ts","sourceRoot":"","sources":["../../../src/helpers/tests/network-adapter-tests.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAA;AAIvF;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAyJ5E;AAID,KAAK,OAAO,GAAG,uBAAuB,GAAG,uBAAuB,EAAE,CAAA;AAElE,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC;IAClC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACrC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB,CAAC,CAAA"}
@@ -15,7 +15,7 @@ import { pause } from "../pause.js";
15
15
  * - `teardown`: An optional function that will be called after the tests have run. This can be used
16
16
  * to clean up any resources that were created during the test.
17
17
  */
18
- export function runAdapterTests(_setup, title) {
18
+ export function runNetworkAdapterTests(_setup, title) {
19
19
  // Wrap the provided setup function
20
20
  const setup = async () => {
21
21
  const { adapters, teardown = NO_OP } = await _setup();
@@ -23,7 +23,7 @@ export function runAdapterTests(_setup, title) {
23
23
  const [a, b, c] = adapters.map(toArray);
24
24
  return { adapters: [a, b, c], teardown };
25
25
  };
26
- describe(`Adapter acceptance tests ${title ? `(${title})` : ""}`, () => {
26
+ describe(`Network adapter acceptance tests ${title ? `(${title})` : ""}`, () => {
27
27
  it("can sync 2 repos", async () => {
28
28
  const doTest = async (a, b) => {
29
29
  const aliceRepo = new Repo({ network: a, peerId: alice });
@@ -0,0 +1,7 @@
1
+ import type { StorageAdapterInterface } from "../../storage/StorageAdapterInterface.js";
2
+ export declare function runStorageAdapterTests(_setup: SetupFn, title?: string): void;
3
+ export type SetupFn = () => Promise<{
4
+ adapter: StorageAdapterInterface;
5
+ teardown?: () => void;
6
+ }>;
7
+ //# sourceMappingURL=storage-adapter-tests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-adapter-tests.d.ts","sourceRoot":"","sources":["../../../src/helpers/tests/storage-adapter-tests.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAA;AAQvF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CA+K5E;AAID,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC;IAClC,OAAO,EAAE,uBAAuB,CAAA;IAChC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB,CAAC,CAAA"}
@@ -0,0 +1,128 @@
1
+ import { describe, expect, it } from "vitest";
2
+ const PAYLOAD_A = () => new Uint8Array([0, 1, 127, 99, 154, 235]);
3
+ const PAYLOAD_B = () => new Uint8Array([1, 76, 160, 53, 57, 10, 230]);
4
+ const PAYLOAD_C = () => new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]);
5
+ const LARGE_PAYLOAD = new Uint8Array(100000).map(() => Math.random() * 256);
6
+ export function runStorageAdapterTests(_setup, title) {
7
+ const setup = async () => {
8
+ const { adapter, teardown = NO_OP } = await _setup();
9
+ return { adapter, teardown };
10
+ };
11
+ describe(`Storage adapter acceptance tests ${title ? `(${title})` : ""}`, () => {
12
+ describe("load", () => {
13
+ it("should return undefined if there is no data", async () => {
14
+ const { adapter, teardown } = await setup();
15
+ const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"]);
16
+ expect(actual).toBeUndefined();
17
+ teardown();
18
+ });
19
+ });
20
+ describe("save and load", () => {
21
+ it("should return data that was saved", async () => {
22
+ const { adapter, teardown } = await setup();
23
+ await adapter.save(["storage-adapter-id"], PAYLOAD_A());
24
+ const actual = await adapter.load(["storage-adapter-id"]);
25
+ expect(actual).toStrictEqual(PAYLOAD_A());
26
+ teardown();
27
+ });
28
+ it("should work with composite keys", async () => {
29
+ const { adapter, teardown } = await setup();
30
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
31
+ const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"]);
32
+ expect(actual).toStrictEqual(PAYLOAD_A());
33
+ teardown();
34
+ });
35
+ it("should work with a large payload", async () => {
36
+ const { adapter, teardown } = await setup();
37
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], LARGE_PAYLOAD);
38
+ const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"]);
39
+ expect(actual).toStrictEqual(LARGE_PAYLOAD);
40
+ teardown();
41
+ });
42
+ });
43
+ describe("loadRange", () => {
44
+ it("should return an empty array if there is no data", async () => {
45
+ const { adapter, teardown } = await setup();
46
+ expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([]);
47
+ teardown();
48
+ });
49
+ });
50
+ describe("save and loadRange", () => {
51
+ it("should return all the data that matches the key", async () => {
52
+ const { adapter, teardown } = await setup();
53
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
54
+ await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B());
55
+ await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C());
56
+ expect(await adapter.loadRange(["AAAAA"])).toStrictEqual(expect.arrayContaining([
57
+ { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() },
58
+ { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B() },
59
+ { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C() },
60
+ ]));
61
+ expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual(expect.arrayContaining([
62
+ { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() },
63
+ { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C() },
64
+ ]));
65
+ teardown();
66
+ });
67
+ it("should only load values that match they key", async () => {
68
+ const { adapter, teardown } = await setup();
69
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
70
+ await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_C());
71
+ const actual = await adapter.loadRange(["AAAAA"]);
72
+ expect(actual).toStrictEqual(expect.arrayContaining([
73
+ { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() },
74
+ ]));
75
+ expect(actual).toStrictEqual(expect.not.arrayContaining([
76
+ { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_C() },
77
+ ]));
78
+ teardown();
79
+ });
80
+ });
81
+ describe("save and remove", () => {
82
+ it("after removing, should be empty", async () => {
83
+ const { adapter, teardown } = await setup();
84
+ await adapter.save(["AAAAA", "snapshot", "xxxxx"], PAYLOAD_A());
85
+ await adapter.remove(["AAAAA", "snapshot", "xxxxx"]);
86
+ expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([]);
87
+ expect(await adapter.load(["AAAAA", "snapshot", "xxxxx"])).toBeUndefined();
88
+ teardown();
89
+ });
90
+ });
91
+ describe("save and save", () => {
92
+ it("should overwrite data saved with the same key", async () => {
93
+ const { adapter, teardown } = await setup();
94
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
95
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B());
96
+ expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual([
97
+ { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B() },
98
+ ]);
99
+ teardown();
100
+ });
101
+ });
102
+ describe("removeRange", () => {
103
+ it("should remove a range of records", async () => {
104
+ const { adapter, teardown } = await setup();
105
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
106
+ await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B());
107
+ await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C());
108
+ await adapter.removeRange(["AAAAA", "sync-state"]);
109
+ expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([
110
+ { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B() },
111
+ ]);
112
+ teardown();
113
+ });
114
+ it("should not remove records that don't match", async () => {
115
+ const { adapter, teardown } = await setup();
116
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
117
+ await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_B());
118
+ await adapter.removeRange(["AAAAA"]);
119
+ const actual = await adapter.loadRange(["BBBBB"]);
120
+ expect(actual).toStrictEqual([
121
+ { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_B() },
122
+ ]);
123
+ teardown();
124
+ });
125
+ });
126
+ });
127
+ }
128
+ const NO_OP = () => { };
@@ -48,5 +48,10 @@ export declare class StorageSubsystem {
48
48
  removeDoc(documentId: DocumentId): Promise<void>;
49
49
  loadSyncState(documentId: DocumentId, storageId: StorageId): Promise<A.SyncState | undefined>;
50
50
  saveSyncState(documentId: DocumentId, storageId: StorageId, syncState: A.SyncState): Promise<void>;
51
+ /**
52
+ * Waiting for document state to be written to disk.
53
+ * @deprecated because it will be changed soon.
54
+ */
55
+ flush(documentId: DocumentId, doc: A.Doc<unknown>, timeout?: number): Promise<void>;
51
56
  }
52
57
  //# sourceMappingURL=StorageSubsystem.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StorageSubsystem.d.ts","sourceRoot":"","sources":["../../src/storage/StorageSubsystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAI9C,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAyB,SAAS,EAAE,MAAM,YAAY,CAAA;AAK7D;;;GAGG;AACH,qBAAa,gBAAgB;;gBAef,cAAc,EAAE,uBAAuB;IAI7C,EAAE,IAAI,OAAO,CAAC,SAAS,CAAC;IA2B9B,kCAAkC;IAC5B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAKlC,gCAAgC;IAC1B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM;IAEX,sCAAsC;IACtC,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,IAAI,CAAC;IAKhB,oCAAoC;IAC9B,MAAM;IACV,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,2FAA2F;IAC3F,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC;IAOhB;;OAEG;IACG,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAmClE;;;;;;OAMG;IACG,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAazE;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,UAAU;IAkEhC,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC;IAM7B,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,CAAC,CAAC,SAAS,GACrB,OAAO,CAAC,IAAI,CAAC;CA8CjB"}
1
+ {"version":3,"file":"StorageSubsystem.d.ts","sourceRoot":"","sources":["../../src/storage/StorageSubsystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAI9C,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAyB,SAAS,EAAE,MAAM,YAAY,CAAA;AAM7D;;;GAGG;AACH,qBAAa,gBAAgB;;gBAiBf,cAAc,EAAE,uBAAuB;IAI7C,EAAE,IAAI,OAAO,CAAC,SAAS,CAAC;IA2B9B,kCAAkC;IAC5B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAKlC,gCAAgC;IAC1B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM;IAEX,sCAAsC;IACtC,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,IAAI,CAAC;IAKhB,oCAAoC;IAC9B,MAAM;IACV,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,2FAA2F;IAC3F,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC;IAOhB;;OAEG;IACG,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAmClE;;;;;;OAMG;IACG,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAczE;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,UAAU;IAkEhC,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC;IAM7B,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,CAAC,CAAC,SAAS,GACrB,OAAO,CAAC,IAAI,CAAC;IAKhB;;;OAGG;IACG,KAAK,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM;CAiE1E"}
@@ -5,6 +5,7 @@ import { mergeArrays } from "../helpers/mergeArrays.js";
5
5
  import { keyHash, headsHash } from "./keyHash.js";
6
6
  import { chunkTypeFromKey } from "./chunkTypeFromKey.js";
7
7
  import * as Uuid from "uuid";
8
+ import { EventEmitter } from "eventemitter3";
8
9
  /**
9
10
  * The storage subsystem is responsible for saving and loading Automerge documents to and from
10
11
  * storage adapter. It also provides a generic key/value storage interface for other uses.
@@ -19,6 +20,7 @@ export class StorageSubsystem {
19
20
  /** Flag to avoid compacting when a compaction is already underway */
20
21
  #compacting = false;
21
22
  #log = debug(`automerge-repo:storage-subsystem`);
23
+ #saved = new EventEmitter();
22
24
  constructor(storageAdapter) {
23
25
  this.#storageAdapter = storageAdapter;
24
26
  }
@@ -124,6 +126,7 @@ export class StorageSubsystem {
124
126
  await this.#saveIncremental(documentId, doc);
125
127
  }
126
128
  this.#storedHeads.set(documentId, A.getHeads(doc));
129
+ this.#saved.emit("saved");
127
130
  }
128
131
  /**
129
132
  * Removes the Automerge document with the given ID from storage
@@ -185,6 +188,30 @@ export class StorageSubsystem {
185
188
  const key = [documentId, "sync-state", storageId];
186
189
  await this.#storageAdapter.save(key, A.encodeSyncState(syncState));
187
190
  }
191
+ /**
192
+ * Waiting for document state to be written to disk.
193
+ * @deprecated because it will be changed soon.
194
+ */
195
+ async flush(documentId, doc, timeout) {
196
+ return new Promise((resolve, reject) => {
197
+ let timeoutId;
198
+ if (timeout) {
199
+ timeoutId = setTimeout(() => {
200
+ this.#saved.off("saved", checkIfSaved);
201
+ reject(new Error("Timed out waiting for save"));
202
+ }, timeout);
203
+ }
204
+ const checkIfSaved = () => {
205
+ if (!this.#shouldSave(documentId, doc)) {
206
+ this.#saved.off("saved", checkIfSaved);
207
+ clearTimeout(timeoutId);
208
+ resolve();
209
+ }
210
+ };
211
+ this.#saved.on("saved", checkIfSaved);
212
+ checkIfSaved();
213
+ });
214
+ }
188
215
  /**
189
216
  * Returns true if the document has changed since the last time it was saved.
190
217
  */
@@ -152,7 +152,7 @@ export class DocSynchronizer extends Synchronizer {
152
152
  return this.#peers.includes(peerId);
153
153
  }
154
154
  beginSync(peerIds) {
155
- const noPeersWithDocument = peerIds.every((peerId) => this.#peerDocumentStatuses[peerId] in ["unavailable", "wants"]);
155
+ const noPeersWithDocument = peerIds.every(peerId => this.#peerDocumentStatuses[peerId] in ["unavailable", "wants"]);
156
156
  // At this point if we don't have anything in our storage, we need to use an empty doc to sync
157
157
  // with; but we don't want to surface that state to the front end
158
158
  const docPromise = this.#handle
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automerge/automerge-repo",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "A repository object to manage a collection of automerge documents",
5
5
  "repository": "https://github.com/automerge/automerge-repo/tree/master/packages/automerge-repo",
6
6
  "author": "Peter van Hardenberg <pvh@pvh.ca>",
@@ -55,5 +55,5 @@
55
55
  "publishConfig": {
56
56
  "access": "public"
57
57
  },
58
- "gitHead": "f59ba3612d1110003e2365fe0fe5a799539f1b59"
58
+ "gitHead": "64edfaea5e53e77cd9158fc1df52fea85801db71"
59
59
  }
package/src/DocHandle.ts CHANGED
@@ -244,12 +244,14 @@ export class DocHandle<T> //
244
244
 
245
245
  /** Returns a promise that resolves when the docHandle is in one of the given states */
246
246
  #statePromise(awaitStates: HandleState | HandleState[]) {
247
- const awaitStatesArray = Array.isArray(awaitStates) ? awaitStates : [awaitStates]
247
+ const awaitStatesArray = Array.isArray(awaitStates)
248
+ ? awaitStates
249
+ : [awaitStates]
248
250
  return waitFor(
249
251
  this.#machine,
250
- s => awaitStatesArray.some((state) => s.matches(state)),
252
+ s => awaitStatesArray.some(state => s.matches(state)),
251
253
  // use a longer delay here so as not to race with other delays
252
- {timeout: this.#timeoutDelay * 2}
254
+ { timeout: this.#timeoutDelay * 2 }
253
255
  )
254
256
  }
255
257
 
package/src/Repo.ts CHANGED
@@ -2,15 +2,18 @@ import { next as Automerge } from "@automerge/automerge"
2
2
  import debug from "debug"
3
3
  import { EventEmitter } from "eventemitter3"
4
4
  import {
5
- generateAutomergeUrl,
6
- interpretAsDocumentId,
7
- parseAutomergeUrl,
5
+ generateAutomergeUrl,
6
+ interpretAsDocumentId,
7
+ parseAutomergeUrl,
8
8
  } from "./AutomergeUrl.js"
9
9
  import { DocHandle, DocHandleEncodedChangePayload } from "./DocHandle.js"
10
10
  import { RemoteHeadsSubscriptions } from "./RemoteHeadsSubscriptions.js"
11
11
  import { headsAreSame } from "./helpers/headsAreSame.js"
12
12
  import { throttle } from "./helpers/throttle.js"
13
- import { NetworkAdapterInterface, type PeerMetadata } from "./network/NetworkAdapterInterface.js"
13
+ import {
14
+ NetworkAdapterInterface,
15
+ type PeerMetadata,
16
+ } from "./network/NetworkAdapterInterface.js"
14
17
  import { NetworkSubsystem } from "./network/NetworkSubsystem.js"
15
18
  import { RepoMessage } from "./network/messages.js"
16
19
  import { StorageAdapterInterface } from "./storage/StorageAdapterInterface.js"
@@ -502,6 +505,33 @@ export class Repo extends EventEmitter<RepoEvents> {
502
505
  return this.storageSubsystem.id()
503
506
  }
504
507
  }
508
+
509
+ /**
510
+ * Waits for Repo to finish write changes to disk.
511
+ * @hidden this API is experimental and may change
512
+ * @param documents - if provided, only waits for the specified documents
513
+ * @param timeout - if provided, the maximum time to wait in milliseconds (rejects on timeout)
514
+ * @returns Promise<void>
515
+ */
516
+ async flush(documents?: DocumentId[], timeout?: number): Promise<void> {
517
+ if (!this.storageSubsystem) {
518
+ return Promise.resolve()
519
+ }
520
+ const handles = documents
521
+ ? documents.map(id => this.#handleCache[id])
522
+ : Object.values(this.#handleCache)
523
+ return Promise.all(
524
+ handles.map(async handle => {
525
+ const doc = handle.docSync()
526
+ if (!doc) {
527
+ return
528
+ }
529
+ return this.storageSubsystem!.flush(handle.documentId, doc, timeout)
530
+ })
531
+ ).then(() => {
532
+ /* No-op. To return `voi`d and not `void[]` */
533
+ })
534
+ }
505
535
  }
506
536
 
507
537
  export interface RepoConfig {
@@ -17,7 +17,7 @@ import { pause } from "../pause.js"
17
17
  * - `teardown`: An optional function that will be called after the tests have run. This can be used
18
18
  * to clean up any resources that were created during the test.
19
19
  */
20
- export function runAdapterTests(_setup: SetupFn, title?: string): void {
20
+ export function runNetworkAdapterTests(_setup: SetupFn, title?: string): void {
21
21
  // Wrap the provided setup function
22
22
  const setup = async () => {
23
23
  const { adapters, teardown = NO_OP } = await _setup()
@@ -28,7 +28,9 @@ export function runAdapterTests(_setup: SetupFn, title?: string): void {
28
28
  return { adapters: [a, b, c], teardown }
29
29
  }
30
30
 
31
- describe(`Adapter acceptance tests ${title ? `(${title})` : ""}`, () => {
31
+ describe(`Network adapter acceptance tests ${
32
+ title ? `(${title})` : ""
33
+ }`, () => {
32
34
  it("can sync 2 repos", async () => {
33
35
  const doTest = async (
34
36
  a: NetworkAdapterInterface[],
@@ -0,0 +1,193 @@
1
+ import { describe, expect, it } from "vitest"
2
+
3
+ import type { StorageAdapterInterface } from "../../storage/StorageAdapterInterface.js"
4
+
5
+ const PAYLOAD_A = () => new Uint8Array([0, 1, 127, 99, 154, 235])
6
+ const PAYLOAD_B = () => new Uint8Array([1, 76, 160, 53, 57, 10, 230])
7
+ const PAYLOAD_C = () => new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])
8
+
9
+ const LARGE_PAYLOAD = new Uint8Array(100000).map(() => Math.random() * 256)
10
+
11
+ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void {
12
+ const setup = async () => {
13
+ const { adapter, teardown = NO_OP } = await _setup()
14
+ return { adapter, teardown }
15
+ }
16
+
17
+ describe(`Storage adapter acceptance tests ${
18
+ title ? `(${title})` : ""
19
+ }`, () => {
20
+ describe("load", () => {
21
+ it("should return undefined if there is no data", async () => {
22
+ const { adapter, teardown } = await setup()
23
+
24
+ const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"])
25
+ expect(actual).toBeUndefined()
26
+
27
+ teardown()
28
+ })
29
+ })
30
+
31
+ describe("save and load", () => {
32
+ it("should return data that was saved", async () => {
33
+ const { adapter, teardown } = await setup()
34
+
35
+ await adapter.save(["storage-adapter-id"], PAYLOAD_A())
36
+ const actual = await adapter.load(["storage-adapter-id"])
37
+ expect(actual).toStrictEqual(PAYLOAD_A())
38
+
39
+ teardown()
40
+ })
41
+
42
+ it("should work with composite keys", async () => {
43
+ const { adapter, teardown } = await setup()
44
+
45
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A())
46
+ const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"])
47
+ expect(actual).toStrictEqual(PAYLOAD_A())
48
+
49
+ teardown()
50
+ })
51
+
52
+ it("should work with a large payload", async () => {
53
+ const { adapter, teardown } = await setup()
54
+
55
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], LARGE_PAYLOAD)
56
+ const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"])
57
+ expect(actual).toStrictEqual(LARGE_PAYLOAD)
58
+
59
+ teardown()
60
+ })
61
+ })
62
+
63
+ describe("loadRange", () => {
64
+ it("should return an empty array if there is no data", async () => {
65
+ const { adapter, teardown } = await setup()
66
+
67
+ expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([])
68
+
69
+ teardown()
70
+ })
71
+ })
72
+
73
+ describe("save and loadRange", () => {
74
+ it("should return all the data that matches the key", async () => {
75
+ const { adapter, teardown } = await setup()
76
+
77
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A())
78
+ await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B())
79
+ await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C())
80
+
81
+ expect(await adapter.loadRange(["AAAAA"])).toStrictEqual(
82
+ expect.arrayContaining([
83
+ { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() },
84
+ { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B() },
85
+ { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C() },
86
+ ])
87
+ )
88
+
89
+ expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual(
90
+ expect.arrayContaining([
91
+ { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() },
92
+ { key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C() },
93
+ ])
94
+ )
95
+
96
+ teardown()
97
+ })
98
+
99
+ it("should only load values that match they key", async () => {
100
+ const { adapter, teardown } = await setup()
101
+
102
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A())
103
+ await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_C())
104
+
105
+ const actual = await adapter.loadRange(["AAAAA"])
106
+ expect(actual).toStrictEqual(
107
+ expect.arrayContaining([
108
+ { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() },
109
+ ])
110
+ )
111
+ expect(actual).toStrictEqual(
112
+ expect.not.arrayContaining([
113
+ { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_C() },
114
+ ])
115
+ )
116
+
117
+ teardown()
118
+ })
119
+ })
120
+
121
+ describe("save and remove", () => {
122
+ it("after removing, should be empty", async () => {
123
+ const { adapter, teardown } = await setup()
124
+
125
+ await adapter.save(["AAAAA", "snapshot", "xxxxx"], PAYLOAD_A())
126
+ await adapter.remove(["AAAAA", "snapshot", "xxxxx"])
127
+
128
+ expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([])
129
+ expect(
130
+ await adapter.load(["AAAAA", "snapshot", "xxxxx"])
131
+ ).toBeUndefined()
132
+
133
+ teardown()
134
+ })
135
+ })
136
+
137
+ describe("save and save", () => {
138
+ it("should overwrite data saved with the same key", async () => {
139
+ const { adapter, teardown } = await setup()
140
+
141
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A())
142
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B())
143
+
144
+ expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual([
145
+ { key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B() },
146
+ ])
147
+
148
+ teardown()
149
+ })
150
+ })
151
+
152
+ describe("removeRange", () => {
153
+ it("should remove a range of records", async () => {
154
+ const { adapter, teardown } = await setup()
155
+
156
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A())
157
+ await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B())
158
+ await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C())
159
+
160
+ await adapter.removeRange(["AAAAA", "sync-state"])
161
+
162
+ expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([
163
+ { key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B() },
164
+ ])
165
+
166
+ teardown()
167
+ })
168
+
169
+ it("should not remove records that don't match", async () => {
170
+ const { adapter, teardown } = await setup()
171
+
172
+ await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A())
173
+ await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_B())
174
+
175
+ await adapter.removeRange(["AAAAA"])
176
+
177
+ const actual = await adapter.loadRange(["BBBBB"])
178
+ expect(actual).toStrictEqual([
179
+ { key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_B() },
180
+ ])
181
+
182
+ teardown()
183
+ })
184
+ })
185
+ })
186
+ }
187
+
188
+ const NO_OP = () => {}
189
+
190
+ export type SetupFn = () => Promise<{
191
+ adapter: StorageAdapterInterface
192
+ teardown?: () => void
193
+ }>
@@ -8,6 +8,7 @@ import { ChunkInfo, StorageKey, StorageId } from "./types.js"
8
8
  import { keyHash, headsHash } from "./keyHash.js"
9
9
  import { chunkTypeFromKey } from "./chunkTypeFromKey.js"
10
10
  import * as Uuid from "uuid"
11
+ import { EventEmitter } from "eventemitter3"
11
12
 
12
13
  /**
13
14
  * The storage subsystem is responsible for saving and loading Automerge documents to and from
@@ -28,6 +29,8 @@ export class StorageSubsystem {
28
29
 
29
30
  #log = debug(`automerge-repo:storage-subsystem`)
30
31
 
32
+ #saved = new EventEmitter<{ saved: () => void }>()
33
+
31
34
  constructor(storageAdapter: StorageAdapterInterface) {
32
35
  this.#storageAdapter = storageAdapter
33
36
  }
@@ -156,6 +159,7 @@ export class StorageSubsystem {
156
159
  await this.#saveIncremental(documentId, doc)
157
160
  }
158
161
  this.#storedHeads.set(documentId, A.getHeads(doc))
162
+ this.#saved.emit("saved")
159
163
  }
160
164
 
161
165
  /**
@@ -245,6 +249,34 @@ export class StorageSubsystem {
245
249
  await this.#storageAdapter.save(key, A.encodeSyncState(syncState))
246
250
  }
247
251
 
252
+ /**
253
+ * Waiting for document state to be written to disk.
254
+ * @deprecated because it will be changed soon.
255
+ */
256
+ async flush(documentId: DocumentId, doc: A.Doc<unknown>, timeout?: number) {
257
+ return new Promise<void>((resolve, reject) => {
258
+ let timeoutId: NodeJS.Timeout
259
+ if (timeout) {
260
+ timeoutId = setTimeout(() => {
261
+ this.#saved.off("saved", checkIfSaved)
262
+ reject(new Error("Timed out waiting for save"))
263
+ }, timeout)
264
+ }
265
+
266
+ const checkIfSaved = () => {
267
+ if (!this.#shouldSave(documentId, doc)) {
268
+ this.#saved.off("saved", checkIfSaved)
269
+ clearTimeout(timeoutId)
270
+ resolve()
271
+ }
272
+ }
273
+
274
+ this.#saved.on("saved", checkIfSaved)
275
+
276
+ checkIfSaved()
277
+ })
278
+ }
279
+
248
280
  /**
249
281
  * Returns true if the document has changed since the last time it was saved.
250
282
  */
@@ -228,7 +228,7 @@ export class DocSynchronizer extends Synchronizer {
228
228
 
229
229
  beginSync(peerIds: PeerId[]) {
230
230
  const noPeersWithDocument = peerIds.every(
231
- (peerId) => this.#peerDocumentStatuses[peerId] in ["unavailable", "wants"]
231
+ peerId => this.#peerDocumentStatuses[peerId] in ["unavailable", "wants"]
232
232
  )
233
233
 
234
234
  // At this point if we don't have anything in our storage, we need to use an empty doc to sync
@@ -0,0 +1,11 @@
1
+ import { beforeEach, describe } from "vitest"
2
+ import { DummyStorageAdapter } from "./helpers/DummyStorageAdapter.js"
3
+ import { runStorageAdapterTests } from "../src/helpers/tests/storage-adapter-tests.js"
4
+
5
+ describe("DummyStorageAdapter", () => {
6
+ const setup = async () => ({
7
+ adapter: new DummyStorageAdapter(),
8
+ })
9
+
10
+ runStorageAdapterTests(setup, "DummyStorageAdapter")
11
+ })
package/test/Repo.test.ts CHANGED
@@ -376,8 +376,7 @@ describe("Repo", () => {
376
376
  d.count = 1
377
377
  })
378
378
 
379
- // wait because storage id is not initialized immediately
380
- await pause()
379
+ await repo.flush()
381
380
 
382
381
  const initialKeys = storage.keys()
383
382
 
@@ -460,6 +459,122 @@ describe("Repo", () => {
460
459
  })
461
460
  })
462
461
 
462
+ describe("flush behaviour", () => {
463
+ const setup = () => {
464
+ let blockedSaves = []
465
+ let resume = () => {
466
+ blockedSaves.forEach(resolve => resolve())
467
+ blockedSaves = []
468
+ }
469
+ const pausedStorage = new DummyStorageAdapter()
470
+ {
471
+ const originalSave = pausedStorage.save.bind(pausedStorage)
472
+ pausedStorage.save = async (...args) => {
473
+ await new Promise(resolve => {
474
+ console.log("made a promise", ...args[0])
475
+ blockedSaves.push(resolve)
476
+ })
477
+ await pause(0)
478
+ // otherwise all the save promises resolve together
479
+ // which prevents testing flushing a single docID
480
+ console.log("resuming save", ...args[0])
481
+ return originalSave(...args)
482
+ }
483
+ }
484
+
485
+ const repo = new Repo({
486
+ storage: pausedStorage,
487
+ network: [],
488
+ })
489
+
490
+ // Create a pair of handles
491
+ const handle = repo.create<{ foo: string }>({ foo: "first" })
492
+ const handle2 = repo.create<{ foo: string }>({ foo: "second" })
493
+ return { resume, pausedStorage, repo, handle, handle2 }
494
+ }
495
+
496
+ it("should not be in a new repo yet because the storage is slow", async () => {
497
+ const { pausedStorage, repo, handle, handle2 } = setup()
498
+ expect((await handle.doc()).foo).toEqual("first")
499
+ expect((await handle2.doc()).foo).toEqual("second")
500
+
501
+ // Reload repo
502
+ const repo2 = new Repo({
503
+ storage: pausedStorage,
504
+ network: [],
505
+ })
506
+
507
+ // Could not find the document that is not yet saved because of slow storage.
508
+ const reloadedHandle = repo2.find<{ foo: string }>(handle.url)
509
+ expect(pausedStorage.keys()).to.deep.equal([])
510
+ expect(await reloadedHandle.doc()).toEqual(undefined)
511
+ })
512
+
513
+ it("should be visible to a new repo after flush()", async () => {
514
+ const { resume, pausedStorage, repo, handle, handle2 } = setup()
515
+
516
+ const flushPromise = repo.flush()
517
+ resume()
518
+ await flushPromise
519
+
520
+ // Check that the data is now saved.
521
+ expect(pausedStorage.keys().length).toBeGreaterThan(0)
522
+
523
+ {
524
+ // Reload repo
525
+ const repo = new Repo({
526
+ storage: pausedStorage,
527
+ network: [],
528
+ })
529
+
530
+ expect(
531
+ (await repo.find<{ foo: string }>(handle.documentId).doc()).foo
532
+ ).toEqual("first")
533
+ expect(
534
+ (await repo.find<{ foo: string }>(handle2.documentId).doc()).foo
535
+ ).toEqual("second")
536
+ }
537
+ })
538
+
539
+ it("should only block on flushing requested documents", async () => {
540
+ const { resume, pausedStorage, repo, handle, handle2 } = setup()
541
+
542
+ const flushPromise = repo.flush([handle.documentId])
543
+ resume()
544
+ await flushPromise
545
+
546
+ // Check that the data is now saved.
547
+ expect(pausedStorage.keys().length).toBeGreaterThan(0)
548
+
549
+ {
550
+ // Reload repo
551
+ const repo = new Repo({
552
+ storage: pausedStorage,
553
+ network: [],
554
+ })
555
+
556
+ expect(
557
+ (await repo.find<{ foo: string }>(handle.documentId).doc()).foo
558
+ ).toEqual("first")
559
+ // Really, it's okay if the second one is also flushed but I'm forcing the issue
560
+ // in the test storage engine above to make sure the behaviour is as documented
561
+ expect(
562
+ await repo.find<{ foo: string }>(handle2.documentId).doc()
563
+ ).toEqual(undefined)
564
+ }
565
+ })
566
+
567
+ it("should time out with failure after a specified delay", async () => {
568
+ const { resume, pausedStorage, repo, handle, handle2 } = setup()
569
+
570
+ const flushPromise = repo.flush([handle.documentId], 10)
571
+ expect(flushPromise).rejects.toThrowError("Timed out waiting for save")
572
+
573
+ // Check that the data is now saved.
574
+ expect(pausedStorage.keys().length).toBe(0)
575
+ })
576
+ })
577
+
463
578
  describe("with peers (linear network)", async () => {
464
579
  it("n-peers connected in a line", async () => {
465
580
  const createNConnectedRepos = async (
@@ -1,18 +1,18 @@
1
- import { pause } from "../../src/helpers/pause.js";
1
+ import { pause } from "../../src/helpers/pause.js"
2
2
  import { Message, NetworkAdapter, PeerId } from "../../src/index.js"
3
3
 
4
4
  export class DummyNetworkAdapter extends NetworkAdapter {
5
5
  #startReady: boolean
6
- #sendMessage?: SendMessageFn;
6
+ #sendMessage?: SendMessageFn
7
7
 
8
- constructor(opts: Options = {startReady: true}) {
8
+ constructor(opts: Options = { startReady: true }) {
9
9
  super()
10
- this.#startReady = opts.startReady;
11
- this.#sendMessage = opts.sendMessage;
10
+ this.#startReady = opts.startReady
11
+ this.#sendMessage = opts.sendMessage
12
12
  }
13
13
 
14
14
  connect(peerId: PeerId) {
15
- this.peerId = peerId;
15
+ this.peerId = peerId
16
16
  if (this.#startReady) {
17
17
  this.emit("ready", { network: this })
18
18
  }
@@ -21,34 +21,36 @@ export class DummyNetworkAdapter extends NetworkAdapter {
21
21
  disconnect() {}
22
22
 
23
23
  peerCandidate(peerId: PeerId) {
24
- this.emit('peer-candidate', { peerId, peerMetadata: {} });
24
+ this.emit("peer-candidate", { peerId, peerMetadata: {} })
25
25
  }
26
26
 
27
27
  override send(message: Message) {
28
- this.#sendMessage?.(message);
28
+ this.#sendMessage?.(message)
29
29
  }
30
30
 
31
31
  receive(message: Message) {
32
- this.emit('message', message);
32
+ this.emit("message", message)
33
33
  }
34
34
 
35
- static createConnectedPair({ latency = 10 }: { latency?: number} = {}) {
35
+ static createConnectedPair({ latency = 10 }: { latency?: number } = {}) {
36
36
  const adapter1: DummyNetworkAdapter = new DummyNetworkAdapter({
37
37
  startReady: true,
38
- sendMessage: (message: Message) => pause(latency).then(() => adapter2.receive(message)),
39
- });
38
+ sendMessage: (message: Message) =>
39
+ pause(latency).then(() => adapter2.receive(message)),
40
+ })
40
41
  const adapter2: DummyNetworkAdapter = new DummyNetworkAdapter({
41
42
  startReady: true,
42
- sendMessage: (message: Message) => pause(latency).then(() => adapter1.receive(message)),
43
- });
43
+ sendMessage: (message: Message) =>
44
+ pause(latency).then(() => adapter1.receive(message)),
45
+ })
44
46
 
45
- return [adapter1, adapter2];
47
+ return [adapter1, adapter2]
46
48
  }
47
49
  }
48
50
 
49
- type SendMessageFn = (message: Message) => void;
51
+ type SendMessageFn = (message: Message) => void
50
52
 
51
53
  type Options = {
52
- startReady?: boolean;
53
- sendMessage?: SendMessageFn;
54
+ startReady?: boolean
55
+ sendMessage?: SendMessageFn
54
56
  }
@@ -1,4 +1,8 @@
1
- import { Chunk, StorageAdapterInterface, type StorageKey } from "../../src/index.js"
1
+ import {
2
+ Chunk,
3
+ StorageAdapterInterface,
4
+ type StorageKey,
5
+ } from "../../src/index.js"
2
6
 
3
7
  export class DummyStorageAdapter implements StorageAdapterInterface {
4
8
  #data: Record<string, Uint8Array> = {}