@automerge/automerge-repo 1.1.0-alpha.6 → 1.1.0

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 (59) hide show
  1. package/README.md +12 -7
  2. package/dist/AutomergeUrl.js +2 -2
  3. package/dist/DocHandle.d.ts +10 -4
  4. package/dist/DocHandle.d.ts.map +1 -1
  5. package/dist/DocHandle.js +17 -8
  6. package/dist/RemoteHeadsSubscriptions.js +3 -3
  7. package/dist/Repo.d.ts +23 -6
  8. package/dist/Repo.d.ts.map +1 -1
  9. package/dist/Repo.js +104 -71
  10. package/dist/helpers/debounce.js +1 -1
  11. package/dist/helpers/pause.d.ts +0 -1
  12. package/dist/helpers/pause.d.ts.map +1 -1
  13. package/dist/helpers/pause.js +2 -8
  14. package/dist/helpers/throttle.js +1 -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 +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/network/NetworkAdapter.d.ts.map +1 -1
  21. package/dist/network/NetworkAdapter.js +2 -1
  22. package/dist/network/NetworkSubsystem.d.ts.map +1 -1
  23. package/dist/network/NetworkSubsystem.js +5 -3
  24. package/dist/network/messages.d.ts +43 -38
  25. package/dist/network/messages.d.ts.map +1 -1
  26. package/dist/network/messages.js +7 -9
  27. package/dist/storage/StorageSubsystem.js +1 -1
  28. package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
  29. package/dist/synchronizer/CollectionSynchronizer.js +1 -0
  30. package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
  31. package/dist/synchronizer/DocSynchronizer.js +13 -5
  32. package/dist/synchronizer/Synchronizer.d.ts +11 -3
  33. package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
  34. package/package.json +4 -4
  35. package/src/AutomergeUrl.ts +2 -2
  36. package/src/DocHandle.ts +34 -12
  37. package/src/RemoteHeadsSubscriptions.ts +3 -3
  38. package/src/Repo.ts +130 -81
  39. package/src/helpers/debounce.ts +1 -1
  40. package/src/helpers/pause.ts +3 -11
  41. package/src/helpers/throttle.ts +1 -1
  42. package/src/helpers/withTimeout.ts +2 -0
  43. package/src/index.ts +1 -1
  44. package/src/network/NetworkAdapter.ts +5 -3
  45. package/src/network/NetworkSubsystem.ts +5 -4
  46. package/src/network/messages.ts +60 -63
  47. package/src/storage/StorageSubsystem.ts +1 -1
  48. package/src/synchronizer/CollectionSynchronizer.ts +2 -1
  49. package/src/synchronizer/DocSynchronizer.ts +19 -11
  50. package/src/synchronizer/Synchronizer.ts +11 -3
  51. package/test/CollectionSynchronizer.test.ts +7 -5
  52. package/test/DocHandle.test.ts +11 -2
  53. package/test/RemoteHeadsSubscriptions.test.ts +53 -50
  54. package/test/Repo.test.ts +64 -2
  55. package/test/StorageSubsystem.test.ts +1 -1
  56. package/test/helpers/collectMessages.ts +19 -0
  57. package/test/remoteHeads.test.ts +141 -120
  58. package/.eslintrc +0 -28
  59. package/test/helpers/waitForMessages.ts +0 -22
package/README.md CHANGED
@@ -45,6 +45,10 @@ A `Repo` exposes these methods:
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
+ - `import(binary: Uint8Array)`
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.
48
52
  - `.on("document", ({handle: DocHandle}) => void)`
49
53
  Registers a callback to be fired each time a new document is loaded or created.
50
54
  - `.on("delete-document", ({handle: DocHandle}) => void)`
@@ -64,7 +68,7 @@ the document.
64
68
 
65
69
  A `DocHandle` also emits these events:
66
70
 
67
- - `change({handle: DocHandle, patches: Patch[], patchInfo: PatchInfo})`
71
+ - `change({handle: DocHandle, patches: Patch[], patchInfo: PatchInfo})`
68
72
  Called whenever the document changes, the handle's .doc
69
73
  - `delete`
70
74
  Called when the document is deleted locally.
@@ -85,11 +89,12 @@ network adapter:
85
89
  const repo = new Repo({
86
90
  network: [new BroadcastChannelNetworkAdapter()],
87
91
  storage: new IndexedDBStorageAdapter(),
88
- sharePolicy: async (peerId: PeerId, documentId: DocumentId) => true // this is the default
92
+ sharePolicy: async (peerId: PeerId, documentId: DocumentId) => true, // this is the default
89
93
  })
90
94
  ```
91
95
 
92
96
  ### Share Policy
97
+
93
98
  The share policy is used to determine which document in your repo should be _automatically_ shared with other peers. **The default setting is to share all documents with all peers.**
94
99
 
95
100
  > **Warning**
@@ -99,7 +104,6 @@ You can override this by providing a custom share policy. The function should re
99
104
 
100
105
  The share policy will not stop a document being _requested_ by another peer by its `DocumentId`.
101
106
 
102
- ```ts
103
107
  ## Starting the demo app
104
108
 
105
109
  ```bash
@@ -272,7 +276,8 @@ you'll need to manually copy the `rootDocId` value between the browsers.)
272
276
  Originally authored by Peter van Hardenberg.
273
277
 
274
278
  With gratitude for contributions by:
275
- - Herb Caudill
276
- - Jeremy Rose
277
- - Alex Currie-Clark
278
- - Dylan Mackenzie
279
+
280
+ - Herb Caudill
281
+ - Jeremy Rose
282
+ - Alex Currie-Clark
283
+ - Dylan Mackenzie
@@ -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)
@@ -21,7 +21,7 @@ export const parseAutomergeUrl = (url) => {
21
21
  * Throws on invalid input.
22
22
  */
23
23
  export const stringifyAutomergeUrl = (arg) => {
24
- let documentId = arg instanceof Uint8Array || typeof arg === "string"
24
+ const documentId = arg instanceof Uint8Array || typeof arg === "string"
25
25
  ? arg
26
26
  : "documentId" in arg
27
27
  ? arg.documentId
@@ -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":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EASL,UAAU,EAEX,MAAM,QAAQ,CAAA;AAMf,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAE9C;;;;;;;;;;;KAWK;AACL,qBAAa,SAAS,CAAC,CAAC,CAAE,EAAE;AAC1B,SAAQ,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;;IAmB/B,UAAU,EAAE,UAAU;IAX/B;;;;OAIG;IACH,IAAI,GAAG,IAAI,YAAY,CAEtB;IAED,cAAc;gBAEL,UAAU,EAAE,UAAU,EAC7B,OAAO,GAAE,gBAAgB,CAAC,CAAC,CAAM;IA0MnC;;;;OAIG;IACH,OAAO,gBAA0C;IACjD;;;;;OAKG;IACH,SAAS,gBAA4C;IACrD,aAAa,gBAAgD;IAC7D,OAAO,WAAY,WAAW,EAAE,aACmB;IAEnD,cAAc;IACd,IAAI,KAAK,eAER;IAED;;;;;OAKG;IACG,SAAS,CAAC,WAAW,GAAE,WAAW,EAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE;;;;;;OAMG;IACG,GAAG,CACP,WAAW,GAAE,WAAW,EAAyB,GAChD,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAYhC;;;;;;;;;OASG;IACH,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS;IAQ/B;;SAEK;IACL,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAM5C;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK;IAKnD,yCAAyC;IACzC,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,GAAG,SAAS;IAIzD,2EAA2E;IAC3E,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAM;IAehE;;;OAGG;IACH,QAAQ,CACN,KAAK,EAAE,CAAC,CAAC,KAAK,EACd,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EACvB,OAAO,GAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAM,GAC/B,MAAM,EAAE,GAAG,SAAS;IAmBvB;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;IAc/B,WAAW;IAIX;;SAEK;IACL,OAAO;IAIP,cAAc;IACd,YAAY;IAIZ,cAAc;IACd,YAAY;IAIZ,kEAAkE;IAClE,MAAM;IAIN;;;;;OAKG;IACH,SAAS,CAAC,OAAO,EAAE,OAAO;CAM3B;AAID,cAAc;AACd,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAE1B;IACE,gGAAgG;IAChG,KAAK,EAAE,IAAI,CAAA;IAEX,yCAAyC;IACzC,YAAY,CAAC,EAAE,CAAC,CAAA;CACjB,GAED;IACE,KAAK,CAAC,EAAE,KAAK,CAAA;IAEb,+HAA+H;IAC/H,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAEL,MAAM,WAAW,uBAAuB;IACtC,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,UAAU,CAAA;IACtB,IAAI,EAAE,UAAU,CAAA;CACjB;AAED,MAAM,WAAW,6BAA6B,CAAC,CAAC;IAC9C,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IACpB,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;CACd;AAED,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;CACrB;AAED,0CAA0C;AAC1C,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,8BAA8B;IAC9B,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IACpB,iDAAiD;IACjD,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IACb,wDAAwD;IACxD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,CAAA;IAClB,mCAAmC;IACnC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;CAC1B;AAED,MAAM,WAAW,gCAAgC,CAAC,CAAC;IACjD,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,wCAAwC,CAAC,CAAC;IACzD,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IACpB,IAAI,EAAE,UAAU,CAAA;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,SAAS,CAAA;IACpB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAA;CACf;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,CAAC,CAAC,SAAS,CAAA;CACvB;AAED,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,eAAe,EAAE,CAAC,OAAO,EAAE,6BAA6B,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IACpE,MAAM,EAAE,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IACpD,MAAM,EAAE,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IACpD,WAAW,EAAE,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IACzD,mBAAmB,EAAE,CAAC,OAAO,EAAE,gCAAgC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAC3E,4BAA4B,EAAE,CAC5B,OAAO,EAAE,wCAAwC,CAAC,CAAC,CAAC,KACjD,IAAI,CAAA;IACT,cAAc,EAAE,CAAC,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAA;CAC/D;AAMD;;;;GAIG;AACH,eAAO,MAAM,WAAW;IACtB,kEAAkE;;IAElE,mDAAmD;;IAEnD,sDAAsD;;IAEtD,6EAA6E;;IAE7E,gCAAgC;;IAEhC,kDAAkD;;IAElD,4EAA4E;;CAEpE,CAAA;AACV,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAA;AAkBxE,eAAO,MAAM,KAAK;;;;;;;;;;;CAWR,CAAA;AA8CV,eAAO,MACL,IAAI,UACJ,OAAO,aACP,gBAAgB,qBAChB,UAAU,gBACV,KAAK,WACL,OAAO,aACP,WAAW,eACE,CAAA"}
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.
@@ -248,7 +248,7 @@ export class RemoteHeadsSubscriptions extends EventEmitter {
248
248
  }
249
249
  }
250
250
  #isPeerSubscribedToDoc(peerId, documentId) {
251
- let subscribedDocs = this.#subscribedDocsByPeer.get(peerId);
251
+ const subscribedDocs = this.#subscribedDocsByPeer.get(peerId);
252
252
  return subscribedDocs && subscribedDocs.has(documentId);
253
253
  }
254
254
  /** Returns the (document, storageId) pairs which have changed after processing msg */
@@ -262,11 +262,11 @@ export class RemoteHeadsSubscriptions extends EventEmitter {
262
262
  }
263
263
  let remote = this.#knownHeads.get(documentId);
264
264
  if (!remote) {
265
- remote = new Map([[storageId, { heads, timestamp }]]);
265
+ remote = new Map();
266
266
  this.#knownHeads.set(documentId, remote);
267
267
  }
268
268
  const docRemote = remote.get(storageId);
269
- if (docRemote && docRemote.timestamp > timestamp) {
269
+ if (docRemote && docRemote.timestamp >= timestamp) {
270
270
  continue;
271
271
  }
272
272
  else {
package/dist/Repo.d.ts CHANGED
@@ -4,8 +4,8 @@ import { NetworkAdapter, type PeerMetadata } from "./network/NetworkAdapter.js";
4
4
  import { NetworkSubsystem } from "./network/NetworkSubsystem.js";
5
5
  import { StorageAdapter } from "./storage/StorageAdapter.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,19 @@ 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>;
78
+ /**
79
+ * Imports document binary into the repo.
80
+ * @param binary - The binary to import
81
+ */
82
+ import<T>(binary: Uint8Array): DocHandle<T>;
70
83
  subscribeToRemotes: (remotes: StorageId[]) => void;
71
84
  storageId: () => Promise<StorageId | undefined>;
72
85
  }
@@ -85,6 +98,10 @@ export interface RepoConfig {
85
98
  * all peers). A server only syncs documents that a peer explicitly requests by ID.
86
99
  */
87
100
  sharePolicy?: SharePolicy;
101
+ /**
102
+ * Whether to enable the experimental remote heads gossiping feature
103
+ */
104
+ enableRemoteHeadsGossiping?: boolean;
88
105
  }
89
106
  /** A function that determines whether we should share a document with a peer
90
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
@@ -48,7 +50,7 @@ export class Repo extends EventEmitter {
48
50
  const saveFn = ({ handle, doc, }) => {
49
51
  void storageSubsystem.saveDoc(handle.documentId, doc);
50
52
  };
51
- const debouncedSaveFn = handle.on("heads-changed", throttle(saveFn, this.saveDebounceRate));
53
+ handle.on("heads-changed", throttle(saveFn, this.saveDebounceRate));
52
54
  if (isNew) {
53
55
  // this is a new document, immediately save it
54
56
  await storageSubsystem.saveDoc(handle.documentId, handle.docSync());
@@ -101,16 +103,20 @@ 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(async (resolve, reject) => resolve({
117
+ const myPeerMetadata = new Promise(
118
+ // eslint-disable-next-line no-async-promise-executor -- TODO: fix
119
+ async (resolve) => resolve({
114
120
  storageId: await storageSubsystem?.id(),
115
121
  isEphemeral,
116
122
  }));
@@ -124,7 +130,7 @@ export class Repo extends EventEmitter {
124
130
  }
125
131
  this.sharePolicy(peerId)
126
132
  .then(shouldShare => {
127
- if (shouldShare) {
133
+ if (shouldShare && this.#remoteHeadsGossipingEnabled) {
128
134
  this.#remoteHeadsSubscriptions.addGenerousPeer(peerId);
129
135
  }
130
136
  })
@@ -154,47 +160,53 @@ export class Repo extends EventEmitter {
154
160
  (!heads || !headsAreSame(heads, message.syncState.theirHeads));
155
161
  if (haveHeadsChanged) {
156
162
  handle.setRemoteHeads(storageId, message.syncState.theirHeads);
157
- if (storageId) {
163
+ if (storageId && this.#remoteHeadsGossipingEnabled) {
158
164
  this.#remoteHeadsSubscriptions.handleImmediateRemoteHeadsChanged(message.documentId, storageId, message.syncState.theirHeads);
159
165
  }
160
166
  }
161
167
  });
162
- this.#remoteHeadsSubscriptions.on("notify-remote-heads", message => {
163
- this.networkSubsystem.send({
164
- type: "remote-heads-changed",
165
- targetId: message.targetId,
166
- documentId: message.documentId,
167
- newHeads: {
168
- [message.storageId]: {
169
- heads: message.heads,
170
- timestamp: message.timestamp,
171
- },
172
- },
173
- });
174
- });
175
- this.#remoteHeadsSubscriptions.on("change-remote-subs", message => {
176
- this.#log("change-remote-subs", message);
177
- for (const peer of message.peers) {
168
+ if (this.#remoteHeadsGossipingEnabled) {
169
+ this.#remoteHeadsSubscriptions.on("notify-remote-heads", message => {
178
170
  this.networkSubsystem.send({
179
- type: "remote-subscription-change",
180
- targetId: peer,
181
- add: message.add,
182
- remove: message.remove,
171
+ type: "remote-heads-changed",
172
+ targetId: message.targetId,
173
+ documentId: message.documentId,
174
+ newHeads: {
175
+ [message.storageId]: {
176
+ heads: message.heads,
177
+ timestamp: message.timestamp,
178
+ },
179
+ },
183
180
  });
184
- }
185
- });
186
- this.#remoteHeadsSubscriptions.on("remote-heads-changed", message => {
187
- const handle = this.#handleCache[message.documentId];
188
- handle.setRemoteHeads(message.storageId, message.remoteHeads);
189
- });
181
+ });
182
+ this.#remoteHeadsSubscriptions.on("change-remote-subs", message => {
183
+ this.#log("change-remote-subs", message);
184
+ for (const peer of message.peers) {
185
+ this.networkSubsystem.send({
186
+ type: "remote-subscription-change",
187
+ targetId: peer,
188
+ add: message.add,
189
+ remove: message.remove,
190
+ });
191
+ }
192
+ });
193
+ this.#remoteHeadsSubscriptions.on("remote-heads-changed", message => {
194
+ const handle = this.#handleCache[message.documentId];
195
+ handle.setRemoteHeads(message.storageId, message.remoteHeads);
196
+ });
197
+ }
190
198
  }
191
199
  #receiveMessage(message) {
192
200
  switch (message.type) {
193
201
  case "remote-subscription-change":
194
- this.#remoteHeadsSubscriptions.handleControlMessage(message);
202
+ if (this.#remoteHeadsGossipingEnabled) {
203
+ this.#remoteHeadsSubscriptions.handleControlMessage(message);
204
+ }
195
205
  break;
196
206
  case "remote-heads-changed":
197
- this.#remoteHeadsSubscriptions.handleRemoteHeads(message);
207
+ if (this.#remoteHeadsGossipingEnabled) {
208
+ this.#remoteHeadsSubscriptions.handleRemoteHeads(message);
209
+ }
198
210
  break;
199
211
  case "sync":
200
212
  case "request":
@@ -207,35 +219,31 @@ export class Repo extends EventEmitter {
207
219
  }
208
220
  #throttledSaveSyncStateHandlers = {};
209
221
  /** saves sync state throttled per storage id, if a peer doesn't have a storage id it's sync state is not persisted */
210
- #saveSyncState(message) {
222
+ #saveSyncState(payload) {
211
223
  if (!this.storageSubsystem) {
212
224
  return;
213
225
  }
214
- const { storageId, isEphemeral } = this.peerMetadataByPeerId[message.peerId] || {};
226
+ const { storageId, isEphemeral } = this.peerMetadataByPeerId[payload.peerId] || {};
215
227
  if (!storageId || isEphemeral) {
216
228
  return;
217
229
  }
218
230
  let handler = this.#throttledSaveSyncStateHandlers[storageId];
219
231
  if (!handler) {
220
232
  handler = this.#throttledSaveSyncStateHandlers[storageId] = throttle(({ documentId, syncState }) => {
221
- this.storageSubsystem.saveSyncState(documentId, storageId, syncState);
233
+ void this.storageSubsystem.saveSyncState(documentId, storageId, syncState);
222
234
  }, this.saveDebounceRate);
223
235
  }
224
- handler(message);
236
+ handler(payload);
225
237
  }
226
238
  /** Returns an existing handle if we have it; creates one otherwise. */
227
- #getHandle(
228
- /** The documentId of the handle to look up or create */
229
- documentId,
230
- /** If we know we're creating a new document, specify this so we can have access to it immediately */
231
- isNew) {
239
+ #getHandle({ documentId, isNew, initialValue, }) {
232
240
  // If we have the handle cached, return it
233
241
  if (this.#handleCache[documentId])
234
242
  return this.#handleCache[documentId];
235
243
  // If not, create a new handle, cache it, and return it
236
244
  if (!documentId)
237
245
  throw new Error(`Invalid documentId ${documentId}`);
238
- const handle = new DocHandle(documentId, { isNew });
246
+ const handle = new DocHandle(documentId, { isNew, initialValue });
239
247
  this.#handleCache[documentId] = handle;
240
248
  return handle;
241
249
  }
@@ -251,28 +259,18 @@ export class Repo extends EventEmitter {
251
259
  return this.peerMetadataByPeerId[peerId]?.storageId;
252
260
  }
253
261
  /**
254
- * Creates a new document and returns a handle to it. The initial value of the document is
255
- * an empty object `{}`. Its documentId is generated by the system. we emit a `document` event
256
- * to advertise interest in the document.
262
+ * Creates a new document and returns a handle to it. The initial value of the document is an
263
+ * empty object `{}` unless an initial value is provided. Its documentId is generated by the
264
+ * system. we emit a `document` event to advertise interest in the document.
257
265
  */
258
- create() {
259
- // TODO:
260
- // either
261
- // - pass an initial value and do something like this to ensure that you get a valid initial value
262
- // const myInitialValue = {
263
- // tasks: [],
264
- // filter: "all",
265
- //
266
- // const guaranteeInitialValue = (doc: any) => {
267
- // if (!doc.tasks) doc.tasks = []
268
- // if (!doc.filter) doc.filter = "all"
269
- // return { ...myInitialValue, ...doc }
270
- // }
271
- // or
272
- // - pass a "reify" function that takes a `<any>` and returns `<T>`
266
+ create(initialValue) {
273
267
  // Generate a new UUID and store it in the buffer
274
268
  const { documentId } = parseAutomergeUrl(generateAutomergeUrl());
275
- const handle = this.#getHandle(documentId, true);
269
+ const handle = this.#getHandle({
270
+ documentId,
271
+ isNew: true,
272
+ initialValue,
273
+ });
276
274
  this.emit("document", { handle, isNew: true });
277
275
  return handle;
278
276
  }
@@ -327,7 +325,10 @@ export class Repo extends EventEmitter {
327
325
  }
328
326
  return this.#handleCache[documentId];
329
327
  }
330
- const handle = this.#getHandle(documentId, false);
328
+ const handle = this.#getHandle({
329
+ documentId,
330
+ isNew: false,
331
+ });
331
332
  this.emit("document", { handle, isNew: false });
332
333
  return handle;
333
334
  }
@@ -335,14 +336,46 @@ export class Repo extends EventEmitter {
335
336
  /** The url or documentId of the handle to delete */
336
337
  id) {
337
338
  const documentId = interpretAsDocumentId(id);
338
- const handle = this.#getHandle(documentId, false);
339
+ const handle = this.#getHandle({ documentId, isNew: false });
339
340
  handle.delete();
340
341
  delete this.#handleCache[documentId];
341
342
  this.emit("delete-document", { documentId });
342
343
  }
344
+ /**
345
+ * Exports a document to a binary format.
346
+ * @param id - The url or documentId of the handle to export
347
+ *
348
+ * @returns Promise<Uint8Array | undefined> - A Promise containing the binary document,
349
+ * or undefined if the document is unavailable.
350
+ */
351
+ async export(id) {
352
+ const documentId = interpretAsDocumentId(id);
353
+ const handle = this.#getHandle({ documentId, isNew: false });
354
+ const doc = await handle.doc();
355
+ if (!doc)
356
+ return undefined;
357
+ return Automerge.save(doc);
358
+ }
359
+ /**
360
+ * Imports document binary into the repo.
361
+ * @param binary - The binary to import
362
+ */
363
+ import(binary) {
364
+ const doc = Automerge.load(binary);
365
+ const handle = this.create();
366
+ handle.update(() => {
367
+ return Automerge.clone(doc);
368
+ });
369
+ return handle;
370
+ }
343
371
  subscribeToRemotes = (remotes) => {
344
- this.#log("subscribeToRemotes", { remotes });
345
- this.#remoteHeadsSubscriptions.subscribeToRemotes(remotes);
372
+ if (this.#remoteHeadsGossipingEnabled) {
373
+ this.#log("subscribeToRemotes", { remotes });
374
+ this.#remoteHeadsSubscriptions.subscribeToRemotes(remotes);
375
+ }
376
+ else {
377
+ this.#log("WARN: subscribeToRemotes called but remote heads gossiping is not enabled");
378
+ }
346
379
  };
347
380
  storageId = async () => {
348
381
  if (!this.storageSubsystem) {
@@ -15,7 +15,7 @@ export const throttle = (fn, rate) => {
15
15
  return function (...args) {
16
16
  clearTimeout(timeout);
17
17
  timeout = setTimeout(() => {
18
- fn.apply(null, args);
18
+ fn(...args);
19
19
  }, rate);
20
20
  };
21
21
  };
@@ -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 */
@@ -32,7 +32,7 @@ export const throttle = (fn, delay) => {
32
32
  wait = lastCall + delay - Date.now();
33
33
  clearTimeout(timeout);
34
34
  timeout = setTimeout(() => {
35
- fn.apply(null, args);
35
+ fn(...args);
36
36
  lastCall = Date.now();
37
37
  }, wait);
38
38
  };
@@ -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,7 +29,7 @@ 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 { isRepoMessage } from "./network/messages.js";
33
33
  export { StorageAdapter } from "./storage/StorageAdapter.js";
34
34
  /** @hidden **/
35
35
  export * as cbor from "./helpers/cbor.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,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,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"}