@automerge/automerge-repo 1.1.0-alpha.7 → 1.1.1

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.
Files changed (73) hide show
  1. package/README.md +5 -3
  2. package/dist/AutomergeUrl.js +1 -1
  3. package/dist/DocHandle.d.ts +10 -4
  4. package/dist/DocHandle.d.ts.map +1 -1
  5. package/dist/DocHandle.js +21 -13
  6. package/dist/Repo.d.ts +22 -10
  7. package/dist/Repo.d.ts.map +1 -1
  8. package/dist/Repo.js +90 -76
  9. package/dist/helpers/pause.d.ts +0 -1
  10. package/dist/helpers/pause.d.ts.map +1 -1
  11. package/dist/helpers/pause.js +2 -8
  12. package/dist/helpers/tests/network-adapter-tests.d.ts +2 -2
  13. package/dist/helpers/tests/network-adapter-tests.d.ts.map +1 -1
  14. package/dist/helpers/tests/network-adapter-tests.js +16 -1
  15. package/dist/helpers/withTimeout.d.ts.map +1 -1
  16. package/dist/helpers/withTimeout.js +2 -0
  17. package/dist/index.d.ts +4 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/network/NetworkAdapter.d.ts +4 -34
  21. package/dist/network/NetworkAdapter.d.ts.map +1 -1
  22. package/dist/network/NetworkAdapter.js +2 -0
  23. package/dist/network/NetworkAdapterInterface.d.ts +61 -0
  24. package/dist/network/NetworkAdapterInterface.d.ts.map +1 -0
  25. package/dist/network/NetworkAdapterInterface.js +2 -0
  26. package/dist/network/NetworkSubsystem.d.ts +3 -3
  27. package/dist/network/NetworkSubsystem.d.ts.map +1 -1
  28. package/dist/network/NetworkSubsystem.js +7 -5
  29. package/dist/network/messages.d.ts +43 -38
  30. package/dist/network/messages.d.ts.map +1 -1
  31. package/dist/network/messages.js +7 -9
  32. package/dist/storage/StorageAdapter.d.ts +3 -1
  33. package/dist/storage/StorageAdapter.d.ts.map +1 -1
  34. package/dist/storage/StorageAdapter.js +1 -0
  35. package/dist/storage/StorageAdapterInterface.d.ts +30 -0
  36. package/dist/storage/StorageAdapterInterface.d.ts.map +1 -0
  37. package/dist/storage/StorageAdapterInterface.js +1 -0
  38. package/dist/storage/StorageSubsystem.d.ts +2 -2
  39. package/dist/storage/StorageSubsystem.d.ts.map +1 -1
  40. package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
  41. package/dist/synchronizer/CollectionSynchronizer.js +1 -0
  42. package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
  43. package/dist/synchronizer/DocSynchronizer.js +13 -9
  44. package/dist/synchronizer/Synchronizer.d.ts +11 -3
  45. package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
  46. package/package.json +3 -4
  47. package/src/AutomergeUrl.ts +1 -1
  48. package/src/DocHandle.ts +40 -19
  49. package/src/Repo.ts +123 -98
  50. package/src/helpers/pause.ts +3 -11
  51. package/src/helpers/tests/network-adapter-tests.ts +30 -4
  52. package/src/helpers/withTimeout.ts +2 -0
  53. package/src/index.ts +4 -2
  54. package/src/network/NetworkAdapter.ts +9 -45
  55. package/src/network/NetworkAdapterInterface.ts +77 -0
  56. package/src/network/NetworkSubsystem.ts +16 -14
  57. package/src/network/messages.ts +60 -63
  58. package/src/storage/StorageAdapter.ts +3 -1
  59. package/src/storage/StorageAdapterInterface.ts +34 -0
  60. package/src/storage/StorageSubsystem.ts +3 -3
  61. package/src/synchronizer/CollectionSynchronizer.ts +1 -0
  62. package/src/synchronizer/DocSynchronizer.ts +22 -18
  63. package/src/synchronizer/Synchronizer.ts +11 -3
  64. package/test/CollectionSynchronizer.test.ts +7 -5
  65. package/test/DocHandle.test.ts +35 -3
  66. package/test/RemoteHeadsSubscriptions.test.ts +49 -49
  67. package/test/Repo.test.ts +71 -2
  68. package/test/StorageSubsystem.test.ts +1 -1
  69. package/test/helpers/DummyNetworkAdapter.ts +37 -5
  70. package/test/helpers/collectMessages.ts +19 -0
  71. package/test/remoteHeads.test.ts +142 -119
  72. package/.eslintrc +0 -28
  73. package/test/helpers/waitForMessages.ts +0 -22
package/README.md CHANGED
@@ -38,15 +38,17 @@ This library provides two main components: the `Repo` itself, and the `DocHandle
38
38
 
39
39
  A `Repo` exposes these methods:
40
40
 
41
- - `create<T>()`
42
- Creates a new, empty `Automerge.Doc` and returns a `DocHandle` for it.
41
+ - `create<T>(initialValue: T?)`
42
+ Creates a new `Automerge.Doc` and returns a `DocHandle` for it. Accepts an optional initial value for the document. Produces an empty document (potentially violating the type!) otherwise.
43
43
  - `find<T>(docId: DocumentId)`
44
44
  Looks up a given document either on the local machine or (if necessary) over any configured
45
45
  networks.
46
46
  - `delete(docId: DocumentId)`
47
47
  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_.
48
48
  - `import(binary: Uint8Array)`
49
- Imports a document binary (from `Automerge.save(doc)`) into the repo, returning a new handle
49
+ Imports a document binary (from `export()` or `Automerge.save(doc)`) into the repo, returning a new handle
50
+ - `export(docId: DocumentId)`
51
+ 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.
50
52
  - `.on("document", ({handle: DocHandle}) => void)`
51
53
  Registers a callback to be fired each time a new document is loaded or created.
52
54
  - `.on("delete-document", ({handle: DocHandle}) => void)`
@@ -4,7 +4,7 @@ export const urlPrefix = "automerge:";
4
4
  /** Given an Automerge URL, returns the DocumentId in both base58check-encoded form and binary form */
5
5
  export const parseAutomergeUrl = (url) => {
6
6
  const regex = new RegExp(`^${urlPrefix}(\\w+)$`);
7
- const [_, docMatch] = url.match(regex) || [];
7
+ const [, docMatch] = url.match(regex) || [];
8
8
  const documentId = docMatch;
9
9
  const binaryDocumentId = documentIdToBinary(documentId);
10
10
  if (!binaryDocumentId)
@@ -26,7 +26,7 @@ export declare class DocHandle<T>//
26
26
  */
27
27
  get url(): AutomergeUrl;
28
28
  /** @hidden */
29
- constructor(documentId: DocumentId, { isNew, timeoutDelay }?: DocHandleOptions);
29
+ constructor(documentId: DocumentId, options?: DocHandleOptions<T>);
30
30
  /**
31
31
  * Checks if the document is ready for accessing or changes.
32
32
  * Note that for documents already stored locally this occurs before synchronization
@@ -120,10 +120,16 @@ export declare class DocHandle<T>//
120
120
  broadcast(message: unknown): void;
121
121
  }
122
122
  /** @hidden */
123
- export interface DocHandleOptions {
124
- isNew?: boolean;
123
+ export type DocHandleOptions<T> = {
124
+ /** If we know this is a new document (because we're creating it) this should be set to true. */
125
+ isNew: true;
126
+ /** The initial value of the document. */
127
+ initialValue?: T;
128
+ } | {
129
+ isNew?: false;
130
+ /** The number of milliseconds before we mark this document as unavailable if we don't have it and nobody shares it with us. */
125
131
  timeoutDelay?: number;
126
- }
132
+ };
127
133
  export interface DocHandleMessagePayload {
128
134
  destinationId: PeerId;
129
135
  documentId: DocumentId;
@@ -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
@@ -24,7 +24,7 @@ export class DocHandle//
24
24
  documentId;
25
25
  #log;
26
26
  #machine;
27
- #timeoutDelay;
27
+ #timeoutDelay = 60_000;
28
28
  #remoteHeads = {};
29
29
  /** The URL of this document
30
30
  *
@@ -35,17 +35,26 @@ export class DocHandle//
35
35
  return stringifyAutomergeUrl({ documentId: this.documentId });
36
36
  }
37
37
  /** @hidden */
38
- constructor(documentId, { isNew = false, timeoutDelay = 60_000 } = {}) {
38
+ constructor(documentId, options = {}) {
39
39
  super();
40
40
  this.documentId = documentId;
41
- this.#timeoutDelay = timeoutDelay;
42
- this.#log = debug(`automerge-repo:dochandle:${this.documentId.slice(0, 5)}`);
43
- // initial doc
44
- let doc = A.init();
45
- // Make an empty change so that we have something to save to disk
41
+ this.documentId = documentId;
42
+ if ("timeoutDelay" in options && options.timeoutDelay) {
43
+ this.#timeoutDelay = options.timeoutDelay;
44
+ }
45
+ let doc;
46
+ const isNew = "isNew" in options && options.isNew;
46
47
  if (isNew) {
47
- doc = A.emptyChange(doc, {});
48
+ // T should really be constrained to extend `Record<string, unknown>` (an automerge doc can't be
49
+ // e.g. a primitive, an array, etc. - it must be an object). But adding that constraint creates
50
+ // a bunch of other problems elsewhere so for now we'll just cast it here to make Automerge happy.
51
+ doc = A.from(options.initialValue);
52
+ doc = A.emptyChange(doc);
48
53
  }
54
+ else {
55
+ doc = A.init();
56
+ }
57
+ this.#log = debug(`automerge-repo:dochandle:${this.documentId.slice(0, 5)}`);
49
58
  /**
50
59
  * Internally we use a state machine to orchestrate document loading and/or syncing, in order to
51
60
  * avoid requesting data we already have, or surfacing intermediate values to the consumer.
@@ -192,11 +201,10 @@ export class DocHandle//
192
201
  }
193
202
  /** Returns a promise that resolves when the docHandle is in one of the given states */
194
203
  #statePromise(awaitStates) {
195
- if (!Array.isArray(awaitStates))
196
- awaitStates = [awaitStates];
197
- return Promise.any(awaitStates.map(state => waitFor(this.#machine, s => s.matches(state), {
198
- timeout: this.#timeoutDelay * 2, // use a longer delay here so as not to race with other delays
199
- })));
204
+ const awaitStatesArray = Array.isArray(awaitStates) ? awaitStates : [awaitStates];
205
+ return waitFor(this.#machine, s => awaitStatesArray.some((state) => s.matches(state)),
206
+ // use a longer delay here so as not to race with other delays
207
+ { timeout: this.#timeoutDelay * 2 });
200
208
  }
201
209
  // PUBLIC
202
210
  /**
package/dist/Repo.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { EventEmitter } from "eventemitter3";
2
2
  import { DocHandle } from "./DocHandle.js";
3
- import { NetworkAdapter, type PeerMetadata } from "./network/NetworkAdapter.js";
3
+ import { NetworkAdapterInterface, type PeerMetadata } from "./network/NetworkAdapterInterface.js";
4
4
  import { NetworkSubsystem } from "./network/NetworkSubsystem.js";
5
- import { StorageAdapter } from "./storage/StorageAdapter.js";
5
+ import { StorageAdapterInterface } from "./storage/StorageAdapterInterface.js";
6
6
  import { StorageSubsystem } from "./storage/StorageSubsystem.js";
7
- import type { AnyDocumentId, DocumentId, PeerId } from "./types.js";
8
7
  import { StorageId } from "./storage/types.js";
8
+ import type { AnyDocumentId, DocumentId, PeerId } from "./types.js";
9
9
  /** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
10
10
  /** The `Repo` is the main entry point of this library
11
11
  *
@@ -29,18 +29,18 @@ export declare class Repo extends EventEmitter<RepoEvents> {
29
29
  /** maps peer id to to persistence information (storageId, isEphemeral), access by collection synchronizer */
30
30
  /** @hidden */
31
31
  peerMetadataByPeerId: Record<PeerId, PeerMetadata>;
32
- constructor({ storage, network, peerId, sharePolicy, isEphemeral, }: RepoConfig);
32
+ constructor({ storage, network, peerId, sharePolicy, isEphemeral, enableRemoteHeadsGossiping, }: RepoConfig);
33
33
  /** Returns all the handles we have cached. */
34
34
  get handles(): Record<DocumentId, DocHandle<any>>;
35
35
  /** Returns a list of all connected peer ids */
36
36
  get peers(): PeerId[];
37
37
  getStorageIdOfPeer(peerId: PeerId): StorageId | undefined;
38
38
  /**
39
- * Creates a new document and returns a handle to it. The initial value of the document is
40
- * an empty object `{}`. Its documentId is generated by the system. we emit a `document` event
41
- * to advertise interest in the document.
39
+ * Creates a new document and returns a handle to it. The initial value of the document is an
40
+ * empty object `{}` unless an initial value is provided. Its documentId is generated by the
41
+ * system. we emit a `document` event to advertise interest in the document.
42
42
  */
43
- create<T>(): DocHandle<T>;
43
+ create<T>(initialValue?: T): DocHandle<T>;
44
44
  /** Create a new DocHandle by cloning the history of an existing DocHandle.
45
45
  *
46
46
  * @param clonedHandle - The handle to clone
@@ -67,6 +67,14 @@ export declare class Repo extends EventEmitter<RepoEvents> {
67
67
  delete(
68
68
  /** The url or documentId of the handle to delete */
69
69
  id: AnyDocumentId): void;
70
+ /**
71
+ * Exports a document to a binary format.
72
+ * @param id - The url or documentId of the handle to export
73
+ *
74
+ * @returns Promise<Uint8Array | undefined> - A Promise containing the binary document,
75
+ * or undefined if the document is unavailable.
76
+ */
77
+ export(id: AnyDocumentId): Promise<Uint8Array | undefined>;
70
78
  /**
71
79
  * Imports document binary into the repo.
72
80
  * @param binary - The binary to import
@@ -82,14 +90,18 @@ export interface RepoConfig {
82
90
  * Sync state is only persisted for non-ephemeral peers */
83
91
  isEphemeral?: boolean;
84
92
  /** A storage adapter can be provided, or not */
85
- storage?: StorageAdapter;
93
+ storage?: StorageAdapterInterface;
86
94
  /** One or more network adapters must be provided */
87
- network: NetworkAdapter[];
95
+ network: NetworkAdapterInterface[];
88
96
  /**
89
97
  * Normal peers typically share generously with everyone (meaning we sync all our documents with
90
98
  * all peers). A server only syncs documents that a peer explicitly requests by ID.
91
99
  */
92
100
  sharePolicy?: SharePolicy;
101
+ /**
102
+ * Whether to enable the experimental remote heads gossiping feature
103
+ */
104
+ enableRemoteHeadsGossiping?: boolean;
93
105
  }
94
106
  /** A function that determines whether we should share a document with a peer
95
107
  *
@@ -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
@@ -3,12 +3,12 @@ import debug from "debug";
3
3
  import { EventEmitter } from "eventemitter3";
4
4
  import { generateAutomergeUrl, interpretAsDocumentId, parseAutomergeUrl, } from "./AutomergeUrl.js";
5
5
  import { DocHandle } from "./DocHandle.js";
6
+ import { RemoteHeadsSubscriptions } from "./RemoteHeadsSubscriptions.js";
7
+ import { headsAreSame } from "./helpers/headsAreSame.js";
6
8
  import { throttle } from "./helpers/throttle.js";
7
9
  import { NetworkSubsystem } from "./network/NetworkSubsystem.js";
8
10
  import { StorageSubsystem } from "./storage/StorageSubsystem.js";
9
11
  import { CollectionSynchronizer } from "./synchronizer/CollectionSynchronizer.js";
10
- import { RemoteHeadsSubscriptions } from "./RemoteHeadsSubscriptions.js";
11
- import { headsAreSame } from "./helpers/headsAreSame.js";
12
12
  /** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
13
13
  /** The `Repo` is the main entry point of this library
14
14
  *
@@ -35,8 +35,10 @@ export class Repo extends EventEmitter {
35
35
  /** @hidden */
36
36
  peerMetadataByPeerId = {};
37
37
  #remoteHeadsSubscriptions = new RemoteHeadsSubscriptions();
38
- constructor({ storage, network, peerId, sharePolicy, isEphemeral = storage === undefined, }) {
38
+ #remoteHeadsGossipingEnabled = false;
39
+ constructor({ storage, network, peerId, sharePolicy, isEphemeral = storage === undefined, enableRemoteHeadsGossiping = false, }) {
39
40
  super();
41
+ this.#remoteHeadsGossipingEnabled = enableRemoteHeadsGossiping;
40
42
  this.#log = debug(`automerge-repo:repo`);
41
43
  this.sharePolicy = sharePolicy ?? this.sharePolicy;
42
44
  // DOC COLLECTION
@@ -101,21 +103,21 @@ export class Repo extends EventEmitter {
101
103
  this.#log(`sending ${message.type} message to ${message.targetId}`);
102
104
  networkSubsystem.send(message);
103
105
  });
104
- this.#synchronizer.on("open-doc", ({ peerId, documentId }) => {
105
- this.#remoteHeadsSubscriptions.subscribePeerToDoc(peerId, documentId);
106
- });
106
+ if (this.#remoteHeadsGossipingEnabled) {
107
+ this.#synchronizer.on("open-doc", ({ peerId, documentId }) => {
108
+ this.#remoteHeadsSubscriptions.subscribePeerToDoc(peerId, documentId);
109
+ });
110
+ }
107
111
  // STORAGE
108
112
  // The storage subsystem has access to some form of persistence, and deals with save and loading documents.
109
113
  const storageSubsystem = storage ? new StorageSubsystem(storage) : undefined;
110
114
  this.storageSubsystem = storageSubsystem;
111
115
  // NETWORK
112
116
  // The network subsystem deals with sending and receiving messages to and from peers.
113
- const myPeerMetadata = new Promise(
114
- // eslint-disable-next-line no-async-promise-executor -- TODO: fix
115
- async (resolve) => resolve({
117
+ const myPeerMetadata = (async () => ({
116
118
  storageId: await storageSubsystem?.id(),
117
119
  isEphemeral,
118
- }));
120
+ }))();
119
121
  const networkSubsystem = new NetworkSubsystem(network, peerId, myPeerMetadata);
120
122
  this.networkSubsystem = networkSubsystem;
121
123
  // When we get a new peer, register it with the synchronizer
@@ -126,7 +128,7 @@ export class Repo extends EventEmitter {
126
128
  }
127
129
  this.sharePolicy(peerId)
128
130
  .then(shouldShare => {
129
- if (shouldShare) {
131
+ if (shouldShare && this.#remoteHeadsGossipingEnabled) {
130
132
  this.#remoteHeadsSubscriptions.addGenerousPeer(peerId);
131
133
  }
132
134
  })
@@ -156,47 +158,53 @@ export class Repo extends EventEmitter {
156
158
  (!heads || !headsAreSame(heads, message.syncState.theirHeads));
157
159
  if (haveHeadsChanged) {
158
160
  handle.setRemoteHeads(storageId, message.syncState.theirHeads);
159
- if (storageId) {
161
+ if (storageId && this.#remoteHeadsGossipingEnabled) {
160
162
  this.#remoteHeadsSubscriptions.handleImmediateRemoteHeadsChanged(message.documentId, storageId, message.syncState.theirHeads);
161
163
  }
162
164
  }
163
165
  });
164
- this.#remoteHeadsSubscriptions.on("notify-remote-heads", message => {
165
- this.networkSubsystem.send({
166
- type: "remote-heads-changed",
167
- targetId: message.targetId,
168
- documentId: message.documentId,
169
- newHeads: {
170
- [message.storageId]: {
171
- heads: message.heads,
172
- timestamp: message.timestamp,
173
- },
174
- },
175
- });
176
- });
177
- this.#remoteHeadsSubscriptions.on("change-remote-subs", message => {
178
- this.#log("change-remote-subs", message);
179
- for (const peer of message.peers) {
166
+ if (this.#remoteHeadsGossipingEnabled) {
167
+ this.#remoteHeadsSubscriptions.on("notify-remote-heads", message => {
180
168
  this.networkSubsystem.send({
181
- type: "remote-subscription-change",
182
- targetId: peer,
183
- add: message.add,
184
- remove: message.remove,
169
+ type: "remote-heads-changed",
170
+ targetId: message.targetId,
171
+ documentId: message.documentId,
172
+ newHeads: {
173
+ [message.storageId]: {
174
+ heads: message.heads,
175
+ timestamp: message.timestamp,
176
+ },
177
+ },
185
178
  });
186
- }
187
- });
188
- this.#remoteHeadsSubscriptions.on("remote-heads-changed", message => {
189
- const handle = this.#handleCache[message.documentId];
190
- handle.setRemoteHeads(message.storageId, message.remoteHeads);
191
- });
179
+ });
180
+ this.#remoteHeadsSubscriptions.on("change-remote-subs", message => {
181
+ this.#log("change-remote-subs", message);
182
+ for (const peer of message.peers) {
183
+ this.networkSubsystem.send({
184
+ type: "remote-subscription-change",
185
+ targetId: peer,
186
+ add: message.add,
187
+ remove: message.remove,
188
+ });
189
+ }
190
+ });
191
+ this.#remoteHeadsSubscriptions.on("remote-heads-changed", message => {
192
+ const handle = this.#handleCache[message.documentId];
193
+ handle.setRemoteHeads(message.storageId, message.remoteHeads);
194
+ });
195
+ }
192
196
  }
193
197
  #receiveMessage(message) {
194
198
  switch (message.type) {
195
199
  case "remote-subscription-change":
196
- this.#remoteHeadsSubscriptions.handleControlMessage(message);
200
+ if (this.#remoteHeadsGossipingEnabled) {
201
+ this.#remoteHeadsSubscriptions.handleControlMessage(message);
202
+ }
197
203
  break;
198
204
  case "remote-heads-changed":
199
- this.#remoteHeadsSubscriptions.handleRemoteHeads(message);
205
+ if (this.#remoteHeadsGossipingEnabled) {
206
+ this.#remoteHeadsSubscriptions.handleRemoteHeads(message);
207
+ }
200
208
  break;
201
209
  case "sync":
202
210
  case "request":
@@ -209,38 +217,31 @@ export class Repo extends EventEmitter {
209
217
  }
210
218
  #throttledSaveSyncStateHandlers = {};
211
219
  /** saves sync state throttled per storage id, if a peer doesn't have a storage id it's sync state is not persisted */
212
- #saveSyncState(message) {
220
+ #saveSyncState(payload) {
213
221
  if (!this.storageSubsystem) {
214
222
  return;
215
223
  }
216
- const { storageId, isEphemeral } = this.peerMetadataByPeerId[message.peerId] || {};
224
+ const { storageId, isEphemeral } = this.peerMetadataByPeerId[payload.peerId] || {};
217
225
  if (!storageId || isEphemeral) {
218
226
  return;
219
227
  }
220
228
  let handler = this.#throttledSaveSyncStateHandlers[storageId];
221
229
  if (!handler) {
222
230
  handler = this.#throttledSaveSyncStateHandlers[storageId] = throttle(({ documentId, syncState }) => {
223
- this.storageSubsystem.saveSyncState(documentId, storageId, syncState)
224
- .catch(err => {
225
- this.#log("error saving sync state", { err });
226
- });
231
+ void this.storageSubsystem.saveSyncState(documentId, storageId, syncState);
227
232
  }, this.saveDebounceRate);
228
233
  }
229
- handler(message);
234
+ handler(payload);
230
235
  }
231
236
  /** Returns an existing handle if we have it; creates one otherwise. */
232
- #getHandle(
233
- /** The documentId of the handle to look up or create */
234
- documentId,
235
- /** If we know we're creating a new document, specify this so we can have access to it immediately */
236
- isNew) {
237
+ #getHandle({ documentId, isNew, initialValue, }) {
237
238
  // If we have the handle cached, return it
238
239
  if (this.#handleCache[documentId])
239
240
  return this.#handleCache[documentId];
240
241
  // If not, create a new handle, cache it, and return it
241
242
  if (!documentId)
242
243
  throw new Error(`Invalid documentId ${documentId}`);
243
- const handle = new DocHandle(documentId, { isNew });
244
+ const handle = new DocHandle(documentId, { isNew, initialValue });
244
245
  this.#handleCache[documentId] = handle;
245
246
  return handle;
246
247
  }
@@ -256,28 +257,18 @@ export class Repo extends EventEmitter {
256
257
  return this.peerMetadataByPeerId[peerId]?.storageId;
257
258
  }
258
259
  /**
259
- * Creates a new document and returns a handle to it. The initial value of the document is
260
- * an empty object `{}`. Its documentId is generated by the system. we emit a `document` event
261
- * to advertise interest in the document.
260
+ * Creates a new document and returns a handle to it. The initial value of the document is an
261
+ * empty object `{}` unless an initial value is provided. Its documentId is generated by the
262
+ * system. we emit a `document` event to advertise interest in the document.
262
263
  */
263
- create() {
264
- // TODO:
265
- // either
266
- // - pass an initial value and do something like this to ensure that you get a valid initial value
267
- // const myInitialValue = {
268
- // tasks: [],
269
- // filter: "all",
270
- //
271
- // const guaranteeInitialValue = (doc: any) => {
272
- // if (!doc.tasks) doc.tasks = []
273
- // if (!doc.filter) doc.filter = "all"
274
- // return { ...myInitialValue, ...doc }
275
- // }
276
- // or
277
- // - pass a "reify" function that takes a `<any>` and returns `<T>`
264
+ create(initialValue) {
278
265
  // Generate a new UUID and store it in the buffer
279
266
  const { documentId } = parseAutomergeUrl(generateAutomergeUrl());
280
- const handle = this.#getHandle(documentId, true);
267
+ const handle = this.#getHandle({
268
+ documentId,
269
+ isNew: true,
270
+ initialValue,
271
+ });
281
272
  this.emit("document", { handle, isNew: true });
282
273
  return handle;
283
274
  }
@@ -332,7 +323,10 @@ export class Repo extends EventEmitter {
332
323
  }
333
324
  return this.#handleCache[documentId];
334
325
  }
335
- const handle = this.#getHandle(documentId, false);
326
+ const handle = this.#getHandle({
327
+ documentId,
328
+ isNew: false,
329
+ });
336
330
  this.emit("document", { handle, isNew: false });
337
331
  return handle;
338
332
  }
@@ -340,11 +334,26 @@ export class Repo extends EventEmitter {
340
334
  /** The url or documentId of the handle to delete */
341
335
  id) {
342
336
  const documentId = interpretAsDocumentId(id);
343
- const handle = this.#getHandle(documentId, false);
337
+ const handle = this.#getHandle({ documentId, isNew: false });
344
338
  handle.delete();
345
339
  delete this.#handleCache[documentId];
346
340
  this.emit("delete-document", { documentId });
347
341
  }
342
+ /**
343
+ * Exports a document to a binary format.
344
+ * @param id - The url or documentId of the handle to export
345
+ *
346
+ * @returns Promise<Uint8Array | undefined> - A Promise containing the binary document,
347
+ * or undefined if the document is unavailable.
348
+ */
349
+ async export(id) {
350
+ const documentId = interpretAsDocumentId(id);
351
+ const handle = this.#getHandle({ documentId, isNew: false });
352
+ const doc = await handle.doc();
353
+ if (!doc)
354
+ return undefined;
355
+ return Automerge.save(doc);
356
+ }
348
357
  /**
349
358
  * Imports document binary into the repo.
350
359
  * @param binary - The binary to import
@@ -358,8 +367,13 @@ export class Repo extends EventEmitter {
358
367
  return handle;
359
368
  }
360
369
  subscribeToRemotes = (remotes) => {
361
- this.#log("subscribeToRemotes", { remotes });
362
- this.#remoteHeadsSubscriptions.subscribeToRemotes(remotes);
370
+ if (this.#remoteHeadsGossipingEnabled) {
371
+ this.#log("subscribeToRemotes", { remotes });
372
+ this.#remoteHeadsSubscriptions.subscribeToRemotes(remotes);
373
+ }
374
+ else {
375
+ this.#log("WARN: subscribeToRemotes called but remote heads gossiping is not enabled");
376
+ }
363
377
  };
364
378
  storageId = async () => {
365
379
  if (!this.storageSubsystem) {
@@ -1,3 +1,2 @@
1
1
  export declare const pause: (t?: number) => Promise<void>;
2
- export declare function rejectOnTimeout<T>(promise: Promise<T>, millis: number): Promise<T>;
3
2
  //# sourceMappingURL=pause.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pause.d.ts","sourceRoot":"","sources":["../../src/helpers/pause.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,+BAC4C,CAAA;AAE9D,wBAAgB,eAAe,CAAC,CAAC,EAC/B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,CAAC,CAAC,CAOZ"}
1
+ {"version":3,"file":"pause.d.ts","sourceRoot":"","sources":["../../src/helpers/pause.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK,+BAC4C,CAAA"}
@@ -1,9 +1,3 @@
1
+ /* c8 ignore start */
1
2
  export const pause = (t = 0) => new Promise(resolve => setTimeout(() => resolve(), t));
2
- export function rejectOnTimeout(promise, millis) {
3
- return Promise.race([
4
- promise,
5
- pause(millis).then(() => {
6
- throw new Error("timeout exceeded");
7
- }),
8
- ]);
9
- }
3
+ /* c8 ignore end */
@@ -1,4 +1,4 @@
1
- import { type NetworkAdapter } from "../../index.js";
1
+ import type { NetworkAdapterInterface } from "../../network/NetworkAdapterInterface.js";
2
2
  /**
3
3
  * Runs a series of tests against a set of three peers, each represented by one or more instantiated
4
4
  * network adapters.
@@ -12,7 +12,7 @@ import { type NetworkAdapter } from "../../index.js";
12
12
  * to clean up any resources that were created during the test.
13
13
  */
14
14
  export declare function runAdapterTests(_setup: SetupFn, title?: string): void;
15
- type Network = NetworkAdapter | NetworkAdapter[];
15
+ type Network = NetworkAdapterInterface | NetworkAdapterInterface[];
16
16
  export type SetupFn = () => Promise<{
17
17
  adapters: [Network, Network, Network];
18
18
  teardown?: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"network-adapter-tests.d.ts","sourceRoot":"","sources":["../../../src/helpers/tests/network-adapter-tests.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAIlE;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CA8HrE;AAID,KAAK,OAAO,GAAG,cAAc,GAAG,cAAc,EAAE,CAAA;AAEhD,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,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,5 +1,5 @@
1
1
  import assert from "assert";
2
- import { describe, it } from "vitest";
2
+ import { describe, expect, it } from "vitest";
3
3
  import { Repo } from "../../index.js";
4
4
  import { eventPromise, eventPromises } from "../eventPromise.js";
5
5
  import { pause } from "../pause.js";
@@ -110,6 +110,21 @@ export function runAdapterTests(_setup, title) {
110
110
  assert.deepStrictEqual(message, alicePresenceData);
111
111
  teardown();
112
112
  });
113
+ it("emits a peer-candidate event with proper peer metadata when a peer connects", async () => {
114
+ const { adapters, teardown } = await setup();
115
+ const a = adapters[0][0];
116
+ const b = adapters[1][0];
117
+ const bPromise = eventPromise(b, "peer-candidate");
118
+ const aPeerMetadata = { storageId: "a" };
119
+ b.connect("b", { storageId: "b" });
120
+ a.connect("a", aPeerMetadata);
121
+ const peerCandidate = await bPromise;
122
+ expect(peerCandidate).toMatchObject({
123
+ peerId: "a",
124
+ peerMetadata: aPeerMetadata,
125
+ });
126
+ teardown();
127
+ });
113
128
  });
114
129
  }
115
130
  const NO_OP = () => { };
@@ -1 +1 @@
1
- {"version":3,"file":"withTimeout.d.ts","sourceRoot":"","sources":["../../src/helpers/withTimeout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,WAAW,8BAEnB,MAAM,eAcV,CAAA;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B"}
1
+ {"version":3,"file":"withTimeout.d.ts","sourceRoot":"","sources":["../../src/helpers/withTimeout.ts"],"names":[],"mappings":"AACA;;;GAGG;AACH,eAAO,MAAM,WAAW,8BAEnB,MAAM,eAcV,CAAA;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B"}
@@ -1,3 +1,4 @@
1
+ /* c8 ignore start */
1
2
  /**
2
3
  * If `promise` is resolved before `t` ms elapse, the timeout is cleared and the result of the
3
4
  * promise is returned. If the timeout ends first, a `TimeoutError` is thrown.
@@ -20,3 +21,4 @@ export class TimeoutError extends Error {
20
21
  this.name = "TimeoutError";
21
22
  }
22
23
  }
24
+ /* c8 ignore end */
package/dist/index.d.ts CHANGED
@@ -29,13 +29,15 @@ export { DocHandle } from "./DocHandle.js";
29
29
  export { isValidAutomergeUrl, parseAutomergeUrl, stringifyAutomergeUrl, } from "./AutomergeUrl.js";
30
30
  export { Repo } from "./Repo.js";
31
31
  export { NetworkAdapter } from "./network/NetworkAdapter.js";
32
- export { isValidRepoMessage } from "./network/messages.js";
32
+ export type { NetworkAdapterInterface } from "./network/NetworkAdapterInterface.js";
33
+ export { isRepoMessage } from "./network/messages.js";
33
34
  export { StorageAdapter } from "./storage/StorageAdapter.js";
35
+ export type { StorageAdapterInterface } from "./storage/StorageAdapterInterface.js";
34
36
  /** @hidden **/
35
37
  export * as cbor from "./helpers/cbor.js";
36
38
  export type { DocHandleChangePayload, DocHandleDeletePayload, DocHandleEncodedChangePayload, DocHandleEphemeralMessagePayload, DocHandleRemoteHeadsPayload, DocHandleEvents, DocHandleOptions, DocHandleOutboundEphemeralMessagePayload, HandleState, } from "./DocHandle.js";
37
39
  export type { DeleteDocumentPayload, DocumentPayload, RepoConfig, RepoEvents, SharePolicy, } from "./Repo.js";
38
- export type { NetworkAdapterEvents, OpenPayload, PeerCandidatePayload, PeerDisconnectedPayload, PeerMetadata, } from "./network/NetworkAdapter.js";
40
+ export type { NetworkAdapterEvents, OpenPayload, PeerCandidatePayload, PeerDisconnectedPayload, PeerMetadata, } from "./network/NetworkAdapterInterface.js";
39
41
  export type { DocumentUnavailableMessage, EphemeralMessage, Message, RepoMessage, RequestMessage, SyncMessage, } from "./network/messages.js";
40
42
  export type { Chunk, ChunkInfo, ChunkType, StorageKey, StorageId, } from "./storage/types.js";
41
43
  export * from "./types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAE5D,eAAe;AACf,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAA;AAIzC,YAAY,EACV,sBAAsB,EACtB,sBAAsB,EACtB,6BAA6B,EAC7B,gCAAgC,EAChC,2BAA2B,EAC3B,eAAe,EACf,gBAAgB,EAChB,wCAAwC,EACxC,WAAW,GACZ,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAA;AAElB,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,uBAAuB,EACvB,YAAY,GACb,MAAM,6BAA6B,CAAA;AAEpC,YAAY,EACV,0BAA0B,EAC1B,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,cAAc,EACd,WAAW,GACZ,MAAM,uBAAuB,CAAA;AAE9B,YAAY,EACV,KAAK,EACL,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,GACV,MAAM,oBAAoB,CAAA;AAE3B,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AAEnF,eAAe;AACf,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAA;AAIzC,YAAY,EACV,sBAAsB,EACtB,sBAAsB,EACtB,6BAA6B,EAC7B,gCAAgC,EAChC,2BAA2B,EAC3B,eAAe,EACf,gBAAgB,EAChB,wCAAwC,EACxC,WAAW,GACZ,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAA;AAElB,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,uBAAuB,EACvB,YAAY,GACb,MAAM,sCAAsC,CAAA;AAE7C,YAAY,EACV,0BAA0B,EAC1B,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,cAAc,EACd,WAAW,GACZ,MAAM,uBAAuB,CAAA;AAE9B,YAAY,EACV,KAAK,EACL,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,GACV,MAAM,oBAAoB,CAAA;AAE3B,cAAc,YAAY,CAAA"}