@automerge/automerge-repo 1.0.18 → 1.1.0-alpha.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 (47) hide show
  1. package/dist/DocHandle.d.ts +7 -8
  2. package/dist/DocHandle.d.ts.map +1 -1
  3. package/dist/DocHandle.js +15 -20
  4. package/dist/RemoteHeadsSubscriptions.d.ts +41 -0
  5. package/dist/RemoteHeadsSubscriptions.d.ts.map +1 -0
  6. package/dist/RemoteHeadsSubscriptions.js +224 -0
  7. package/dist/Repo.d.ts +15 -1
  8. package/dist/Repo.d.ts.map +1 -1
  9. package/dist/Repo.js +118 -8
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/network/NetworkAdapter.d.ts +8 -1
  13. package/dist/network/NetworkAdapter.d.ts.map +1 -1
  14. package/dist/network/NetworkAdapter.js +2 -0
  15. package/dist/network/NetworkSubsystem.d.ts +7 -1
  16. package/dist/network/NetworkSubsystem.d.ts.map +1 -1
  17. package/dist/network/NetworkSubsystem.js +11 -4
  18. package/dist/network/messages.d.ts +24 -1
  19. package/dist/network/messages.d.ts.map +1 -1
  20. package/dist/network/messages.js +5 -1
  21. package/dist/storage/StorageSubsystem.d.ts +5 -3
  22. package/dist/storage/StorageSubsystem.d.ts.map +1 -1
  23. package/dist/storage/StorageSubsystem.js +17 -4
  24. package/dist/storage/types.d.ts +4 -0
  25. package/dist/storage/types.d.ts.map +1 -1
  26. package/dist/synchronizer/CollectionSynchronizer.d.ts +2 -2
  27. package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
  28. package/dist/synchronizer/CollectionSynchronizer.js +7 -3
  29. package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
  30. package/dist/synchronizer/DocSynchronizer.js +0 -9
  31. package/package.json +3 -3
  32. package/src/DocHandle.ts +17 -22
  33. package/src/RemoteHeadsSubscriptions.ts +306 -0
  34. package/src/Repo.ts +173 -11
  35. package/src/index.ts +1 -0
  36. package/src/network/NetworkAdapter.ts +12 -1
  37. package/src/network/NetworkSubsystem.ts +24 -11
  38. package/src/network/messages.ts +30 -1
  39. package/src/storage/StorageSubsystem.ts +24 -6
  40. package/src/storage/types.ts +3 -0
  41. package/src/synchronizer/CollectionSynchronizer.ts +10 -5
  42. package/src/synchronizer/DocSynchronizer.ts +0 -12
  43. package/test/DocHandle.test.ts +1 -18
  44. package/test/RemoteHeadsSubscriptions.test.ts +343 -0
  45. package/test/Repo.test.ts +51 -15
  46. package/test/StorageSubsystem.test.ts +28 -6
  47. package/test/remoteHeads.test.ts +135 -0
@@ -2,6 +2,7 @@ import * as A from "@automerge/automerge/next";
2
2
  import { EventEmitter } from "eventemitter3";
3
3
  import { StateValue } from "xstate";
4
4
  import type { AutomergeUrl, DocumentId, PeerId } from "./types.js";
5
+ import { StorageId } from "./storage/types.js";
5
6
  /** DocHandle is a wrapper around a single Automerge document that lets us
6
7
  * listen for changes and notify the network and storage of new changes.
7
8
  *
@@ -73,12 +74,12 @@ export declare class DocHandle<T>//
73
74
  * @hidden
74
75
  * */
75
76
  update(callback: (doc: A.Doc<T>) => A.Doc<T>): void;
76
- /** `setRemoteHeads` is called by the doc synchronizer
77
+ /** `setRemoteHeads` is called by the repo either when a doc handle changes or we receive new remote heads
77
78
  * @hidden
78
79
  */
79
- setRemoteHeads(peerId: PeerId, heads: A.Heads): void;
80
- /** Returns the heads of the peer */
81
- getRemoteHeads(peerId: PeerId): A.Heads | undefined;
80
+ setRemoteHeads(storageId: StorageId, heads: A.Heads): void;
81
+ /** Returns the heads of the storageId */
82
+ getRemoteHeads(storageId: StorageId): A.Heads | undefined;
82
83
  /** `change` is called by the repo when the document is changed locally */
83
84
  change(callback: A.ChangeFn<T>, options?: A.ChangeOptions<T>): void;
84
85
  /** Make a change as if the document were at `heads`
@@ -156,7 +157,7 @@ export interface DocHandleOutboundEphemeralMessagePayload<T> {
156
157
  data: Uint8Array;
157
158
  }
158
159
  export interface DocHandleRemoteHeadsPayload {
159
- peerId: PeerId;
160
+ storageId: StorageId;
160
161
  heads: A.Heads;
161
162
  }
162
163
  export interface DocHandleSyncStatePayload {
@@ -188,8 +189,6 @@ export declare const HandleState: {
188
189
  readonly REQUESTING: "requesting";
189
190
  /** The document is available */
190
191
  readonly READY: "ready";
191
- /** We were unable to load or request the document for some reason */
192
- readonly FAILED: "failed";
193
192
  /** The document has been deleted from the repo */
194
193
  readonly DELETED: "deleted";
195
194
  /** The document was not available in storage or from any connected peers */
@@ -208,5 +207,5 @@ export declare const Event: {
208
207
  readonly DELETE: "DELETE";
209
208
  readonly MARK_UNAVAILABLE: "MARK_UNAVAILABLE";
210
209
  };
211
- export declare const IDLE: "idle", LOADING: "loading", AWAITING_NETWORK: "awaitingNetwork", REQUESTING: "requesting", READY: "ready", FAILED: "failed", DELETED: "deleted", UNAVAILABLE: "unavailable";
210
+ export declare const IDLE: "idle", LOADING: "loading", AWAITING_NETWORK: "awaitingNetwork", REQUESTING: "requesting", READY: "ready", DELETED: "deleted", UNAVAILABLE: "unavailable";
212
211
  //# sourceMappingURL=DocHandle.d.ts.map
@@ -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
@@ -50,9 +50,9 @@ export class DocHandle//
50
50
  * Internally we use a state machine to orchestrate document loading and/or syncing, in order to
51
51
  * avoid requesting data we already have, or surfacing intermediate values to the consumer.
52
52
  *
53
- * ┌─────────────────────┬─────────TIMEOUT────►┌────────┐
54
- * ┌───┴─────┐ ┌───┴────────┐ │ failed
55
- * ┌───────┐ ┌──FIND──┤ loading ├─REQUEST──►│ requesting ├─UPDATE──┐ └────────┘
53
+ * ┌─────────────────────┬─────────TIMEOUT────►┌─────────────┐
54
+ * ┌───┴─────┐ ┌───┴────────┐ │ unavailable
55
+ * ┌───────┐ ┌──FIND──┤ loading ├─REQUEST──►│ requesting ├─UPDATE──┐ └─────────────┘
56
56
  * │ idle ├──┤ └───┬─────┘ └────────────┘ │
57
57
  * └───────┘ │ │ └─►┌────────┐
58
58
  * │ └───────LOAD───────────────────────────────►│ ready │
@@ -87,7 +87,7 @@ export class DocHandle//
87
87
  after: [
88
88
  {
89
89
  delay: this.#timeoutDelay,
90
- target: FAILED,
90
+ target: UNAVAILABLE,
91
91
  },
92
92
  ],
93
93
  },
@@ -111,7 +111,7 @@ export class DocHandle//
111
111
  after: [
112
112
  {
113
113
  delay: this.#timeoutDelay,
114
- target: FAILED,
114
+ target: UNAVAILABLE,
115
115
  },
116
116
  ],
117
117
  },
@@ -122,9 +122,6 @@ export class DocHandle//
122
122
  DELETE: { actions: "onDelete", target: DELETED },
123
123
  },
124
124
  },
125
- failed: {
126
- type: "final",
127
- },
128
125
  deleted: {
129
126
  type: "final",
130
127
  },
@@ -198,7 +195,7 @@ export class DocHandle//
198
195
  if (!Array.isArray(awaitStates))
199
196
  awaitStates = [awaitStates];
200
197
  return Promise.any(awaitStates.map(state => waitFor(this.#machine, s => s.matches(state), {
201
- timeout: this.#timeoutDelay * 2000, // longer than the delay above for testing
198
+ timeout: this.#timeoutDelay * 2, // use a longer delay here so as not to race with other delays
202
199
  })));
203
200
  }
204
201
  // PUBLIC
@@ -243,7 +240,7 @@ export class DocHandle//
243
240
  await this.#statePromise(awaitStates);
244
241
  }
245
242
  catch (error) {
246
- // if we timed out (or the load has already failed), return undefined
243
+ // if we timed out (or have determined the document is currently unavailable), return undefined
247
244
  return undefined;
248
245
  }
249
246
  // Return the document
@@ -273,16 +270,16 @@ export class DocHandle//
273
270
  payload: { callback },
274
271
  });
275
272
  }
276
- /** `setRemoteHeads` is called by the doc synchronizer
273
+ /** `setRemoteHeads` is called by the repo either when a doc handle changes or we receive new remote heads
277
274
  * @hidden
278
275
  */
279
- setRemoteHeads(peerId, heads) {
280
- this.#remoteHeads[peerId] = heads;
281
- this.emit("remote-heads", { peerId, heads });
276
+ setRemoteHeads(storageId, heads) {
277
+ this.#remoteHeads[storageId] = heads;
278
+ this.emit("remote-heads", { storageId, heads });
282
279
  }
283
- /** Returns the heads of the peer */
284
- getRemoteHeads(peerId) {
285
- return this.#remoteHeads[peerId];
280
+ /** Returns the heads of the storageId */
281
+ getRemoteHeads(storageId) {
282
+ return this.#remoteHeads[storageId];
286
283
  }
287
284
  /** `change` is called by the repo when the document is changed locally */
288
285
  change(callback, options = {}) {
@@ -396,8 +393,6 @@ export const HandleState = {
396
393
  REQUESTING: "requesting",
397
394
  /** The document is available */
398
395
  READY: "ready",
399
- /** We were unable to load or request the document for some reason */
400
- FAILED: "failed",
401
396
  /** The document has been deleted from the repo */
402
397
  DELETED: "deleted",
403
398
  /** The document was not available in storage or from any connected peers */
@@ -417,5 +412,5 @@ export const Event = {
417
412
  MARK_UNAVAILABLE: "MARK_UNAVAILABLE",
418
413
  };
419
414
  // CONSTANTS
420
- export const { IDLE, LOADING, AWAITING_NETWORK, REQUESTING, READY, FAILED, DELETED, UNAVAILABLE, } = HandleState;
415
+ export const { IDLE, LOADING, AWAITING_NETWORK, REQUESTING, READY, DELETED, UNAVAILABLE, } = HandleState;
421
416
  const { CREATE, FIND, REQUEST, UPDATE, TIMEOUT, DELETE, REQUEST_COMPLETE, MARK_UNAVAILABLE, AWAIT_NETWORK, NETWORK_READY, } = Event;
@@ -0,0 +1,41 @@
1
+ import { next as A } from "@automerge/automerge";
2
+ import { EventEmitter } from "eventemitter3";
3
+ import { DocumentId, PeerId } from "./types.js";
4
+ import { RemoteHeadsChanged, RemoteSubscriptionControlMessage } from "./network/messages.js";
5
+ import { StorageId } from "./index.js";
6
+ export type RemoteHeadsSubscriptionEventPayload = {
7
+ documentId: DocumentId;
8
+ storageId: StorageId;
9
+ remoteHeads: A.Heads;
10
+ timestamp: number;
11
+ };
12
+ export type NotifyRemoteHeadsPayload = {
13
+ targetId: PeerId;
14
+ documentId: DocumentId;
15
+ storageId: StorageId;
16
+ heads: A.Heads;
17
+ timestamp: number;
18
+ };
19
+ type RemoteHeadsSubscriptionEvents = {
20
+ "remote-heads-changed": (payload: RemoteHeadsSubscriptionEventPayload) => void;
21
+ "change-remote-subs": (payload: {
22
+ peers: PeerId[];
23
+ add?: StorageId[];
24
+ remove?: StorageId[];
25
+ }) => void;
26
+ "notify-remote-heads": (payload: NotifyRemoteHeadsPayload) => void;
27
+ };
28
+ export declare class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSubscriptionEvents> {
29
+ #private;
30
+ subscribeToRemotes(remotes: StorageId[]): void;
31
+ unsubscribeFromRemotes(remotes: StorageId[]): void;
32
+ handleControlMessage(control: RemoteSubscriptionControlMessage): void;
33
+ /** A peer we are not directly connected to has changed their heads */
34
+ handleRemoteHeads(msg: RemoteHeadsChanged): void;
35
+ /** A peer we are directly connected to has updated their heads */
36
+ handleImmediateRemoteHeadsChanged(documentId: DocumentId, storageId: StorageId, heads: A.Heads): void;
37
+ addGenerousPeer(peerId: PeerId): void;
38
+ removePeer(peerId: PeerId): void;
39
+ }
40
+ export {};
41
+ //# sourceMappingURL=RemoteHeadsSubscriptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RemoteHeadsSubscriptions.d.ts","sourceRoot":"","sources":["../src/RemoteHeadsSubscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,EACL,kBAAkB,EAClB,gCAAgC,EACjC,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAItC,MAAM,MAAM,mCAAmC,GAAG;IAChD,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;IACpB,WAAW,EAAE,CAAC,CAAC,KAAK,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAGD,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;IACpB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,KAAK,6BAA6B,GAAG;IACnC,sBAAsB,EAAE,CAAC,OAAO,EAAE,mCAAmC,KAAK,IAAI,CAAA;IAC9E,oBAAoB,EAAE,CAAC,OAAO,EAAE;QAC9B,KAAK,EAAE,MAAM,EAAE,CAAA;QACf,GAAG,CAAC,EAAE,SAAS,EAAE,CAAA;QACjB,MAAM,CAAC,EAAE,SAAS,EAAE,CAAA;KACrB,KAAK,IAAI,CAAA;IACV,qBAAqB,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,IAAI,CAAA;CACnE,CAAA;AAED,qBAAa,wBAAyB,SAAQ,YAAY,CAAC,6BAA6B,CAAC;;IAWvF,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE;IAkBvC,sBAAsB,CAAC,OAAO,EAAE,SAAS,EAAE;IAsB3C,oBAAoB,CAAC,OAAO,EAAE,gCAAgC;IA4C9D,sEAAsE;IACtE,iBAAiB,CAAC,GAAG,EAAE,kBAAkB;IA8CzC,kEAAkE;IAClE,iCAAiC,CAC/B,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,CAAC,CAAC,KAAK;IA8BhB,eAAe,CAAC,MAAM,EAAE,MAAM;IAwB9B,UAAU,CAAC,MAAM,EAAE,MAAM;CA+D1B"}
@@ -0,0 +1,224 @@
1
+ import { EventEmitter } from "eventemitter3";
2
+ import debug from "debug";
3
+ export class RemoteHeadsSubscriptions extends EventEmitter {
4
+ // Storage IDs we have received remote heads from
5
+ #knownHeads = new Map();
6
+ // Storage IDs we have subscribed to via Repo.subscribeToRemoteHeads
7
+ #ourSubscriptions = new Set();
8
+ // Storage IDs other peers have subscribed to by sending us a control message
9
+ #theirSubscriptions = new Map();
10
+ // Peers we will always share remote heads with even if they are not subscribed
11
+ #generousPeers = new Set();
12
+ #log = debug("automerge-repo:remote-heads-subscriptions");
13
+ subscribeToRemotes(remotes) {
14
+ this.#log("subscribeToRemotes", remotes);
15
+ const remotesToAdd = [];
16
+ for (const remote of remotes) {
17
+ if (!this.#ourSubscriptions.has(remote)) {
18
+ this.#ourSubscriptions.add(remote);
19
+ remotesToAdd.push(remote);
20
+ }
21
+ }
22
+ if (remotesToAdd.length > 0) {
23
+ this.emit("change-remote-subs", {
24
+ add: remotesToAdd,
25
+ peers: Array.from(this.#generousPeers),
26
+ });
27
+ }
28
+ }
29
+ unsubscribeFromRemotes(remotes) {
30
+ this.#log("subscribeToRemotes", remotes);
31
+ const remotesToRemove = [];
32
+ for (const remote of remotes) {
33
+ if (this.#ourSubscriptions.has(remote)) {
34
+ this.#ourSubscriptions.delete(remote);
35
+ if (!this.#theirSubscriptions.has(remote)) {
36
+ remotesToRemove.push(remote);
37
+ }
38
+ }
39
+ }
40
+ if (remotesToRemove.length > 0) {
41
+ this.emit("change-remote-subs", {
42
+ remove: remotesToRemove,
43
+ peers: Array.from(this.#generousPeers),
44
+ });
45
+ }
46
+ }
47
+ handleControlMessage(control) {
48
+ const remotesToAdd = [];
49
+ const remotesToRemove = [];
50
+ this.#log("handleControlMessage", control);
51
+ if (control.add) {
52
+ for (const remote of control.add) {
53
+ let theirSubs = this.#theirSubscriptions.get(remote);
54
+ if (!theirSubs) {
55
+ theirSubs = new Set();
56
+ this.#theirSubscriptions.set(remote, theirSubs);
57
+ if (!this.#ourSubscriptions.has(remote)) {
58
+ remotesToAdd.push(remote);
59
+ }
60
+ }
61
+ theirSubs.add(control.senderId);
62
+ }
63
+ }
64
+ if (control.remove) {
65
+ for (const remote of control.remove) {
66
+ const theirSubs = this.#theirSubscriptions.get(remote);
67
+ if (theirSubs) {
68
+ theirSubs.delete(control.senderId);
69
+ // if no one is subscribed anymore remove remote
70
+ if (theirSubs.size == 0 && !this.#ourSubscriptions.has(remote)) {
71
+ remotesToRemove.push(remote);
72
+ }
73
+ }
74
+ }
75
+ }
76
+ if (remotesToAdd.length > 0 || remotesToRemove.length > 0) {
77
+ this.emit("change-remote-subs", {
78
+ peers: Array.from(this.#generousPeers),
79
+ add: remotesToAdd,
80
+ remove: remotesToRemove,
81
+ });
82
+ }
83
+ }
84
+ /** A peer we are not directly connected to has changed their heads */
85
+ handleRemoteHeads(msg) {
86
+ this.#log("handleRemoteHeads", msg);
87
+ const changedHeads = this.#changedHeads(msg);
88
+ // Emit a remote-heads-changed event to update local dochandles
89
+ for (const event of changedHeads) {
90
+ if (this.#ourSubscriptions.has(event.storageId)) {
91
+ this.emit("remote-heads-changed", event);
92
+ }
93
+ }
94
+ // Notify generous peers of these changes regardless of if they are subscribed to us
95
+ for (const event of changedHeads) {
96
+ for (const peer of this.#generousPeers) {
97
+ // don't emit event to sender if sender is a generous peer
98
+ if (peer === msg.senderId) {
99
+ continue;
100
+ }
101
+ this.emit("notify-remote-heads", {
102
+ targetId: peer,
103
+ documentId: event.documentId,
104
+ heads: event.remoteHeads,
105
+ timestamp: event.timestamp,
106
+ storageId: event.storageId,
107
+ });
108
+ }
109
+ }
110
+ // Notify subscribers of these changes
111
+ for (const event of changedHeads) {
112
+ const theirSubs = this.#theirSubscriptions.get(event.storageId);
113
+ if (theirSubs) {
114
+ for (const peerId of theirSubs) {
115
+ this.emit("notify-remote-heads", {
116
+ targetId: peerId,
117
+ documentId: event.documentId,
118
+ heads: event.remoteHeads,
119
+ timestamp: event.timestamp,
120
+ storageId: event.storageId,
121
+ });
122
+ }
123
+ }
124
+ }
125
+ }
126
+ /** A peer we are directly connected to has updated their heads */
127
+ handleImmediateRemoteHeadsChanged(documentId, storageId, heads) {
128
+ this.#log("handleLocalHeadsChanged", documentId, storageId, heads);
129
+ const remote = this.#knownHeads.get(documentId);
130
+ const timestamp = Date.now();
131
+ if (!remote) {
132
+ this.#knownHeads.set(documentId, new Map([[storageId, { heads, timestamp }]]));
133
+ }
134
+ else {
135
+ const docRemote = remote.get(storageId);
136
+ if (!docRemote || docRemote.timestamp < Date.now()) {
137
+ remote.set(storageId, { heads, timestamp: Date.now() });
138
+ }
139
+ }
140
+ const theirSubs = this.#theirSubscriptions.get(storageId);
141
+ if (theirSubs) {
142
+ for (const peerId of theirSubs) {
143
+ this.emit("notify-remote-heads", {
144
+ targetId: peerId,
145
+ documentId: documentId,
146
+ heads: heads,
147
+ timestamp: timestamp,
148
+ storageId: storageId,
149
+ });
150
+ }
151
+ }
152
+ }
153
+ addGenerousPeer(peerId) {
154
+ this.#log("addGenerousPeer", peerId);
155
+ this.#generousPeers.add(peerId);
156
+ if (this.#ourSubscriptions.size > 0) {
157
+ this.emit("change-remote-subs", {
158
+ add: Array.from(this.#ourSubscriptions),
159
+ peers: [peerId],
160
+ });
161
+ }
162
+ for (const [documentId, remote] of this.#knownHeads) {
163
+ for (const [storageId, { heads, timestamp }] of remote) {
164
+ this.emit("notify-remote-heads", {
165
+ targetId: peerId,
166
+ documentId: documentId,
167
+ heads: heads,
168
+ timestamp: timestamp,
169
+ storageId: storageId,
170
+ });
171
+ }
172
+ }
173
+ }
174
+ removePeer(peerId) {
175
+ this.#log("removePeer", peerId);
176
+ const remotesToRemove = [];
177
+ this.#generousPeers.delete(peerId);
178
+ for (const [storageId, peerIds] of this.#theirSubscriptions) {
179
+ if (peerIds.has(peerId)) {
180
+ peerIds.delete(peerId);
181
+ if (peerIds.size == 0) {
182
+ remotesToRemove.push(storageId);
183
+ this.#theirSubscriptions.delete(storageId);
184
+ }
185
+ }
186
+ }
187
+ if (remotesToRemove.length > 0) {
188
+ this.emit("change-remote-subs", {
189
+ remove: remotesToRemove,
190
+ peers: Array.from(this.#generousPeers),
191
+ });
192
+ }
193
+ }
194
+ /** Returns the (document, storageId) pairs which have changed after processing msg */
195
+ #changedHeads(msg) {
196
+ const changedHeads = [];
197
+ const { documentId, newHeads } = msg;
198
+ for (const [storageId, { heads, timestamp }] of Object.entries(newHeads)) {
199
+ if (!this.#ourSubscriptions.has(storageId) &&
200
+ !this.#theirSubscriptions.has(storageId)) {
201
+ continue;
202
+ }
203
+ let remote = this.#knownHeads.get(documentId);
204
+ if (!remote) {
205
+ remote = new Map([[storageId, { heads, timestamp }]]);
206
+ this.#knownHeads.set(documentId, remote);
207
+ }
208
+ const docRemote = remote.get(storageId);
209
+ if (docRemote && docRemote.timestamp > timestamp) {
210
+ continue;
211
+ }
212
+ else {
213
+ remote.set(storageId, { timestamp, heads });
214
+ changedHeads.push({
215
+ documentId,
216
+ storageId: storageId,
217
+ remoteHeads: heads,
218
+ timestamp,
219
+ });
220
+ }
221
+ }
222
+ return changedHeads;
223
+ }
224
+ }
package/dist/Repo.d.ts CHANGED
@@ -5,6 +5,7 @@ import { NetworkSubsystem } from "./network/NetworkSubsystem.js";
5
5
  import { StorageAdapter } from "./storage/StorageAdapter.js";
6
6
  import { StorageSubsystem } from "./storage/StorageSubsystem.js";
7
7
  import type { AnyDocumentId, DocumentId, PeerId } from "./types.js";
8
+ import { StorageId } from "./storage/types.js";
8
9
  /** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
9
10
  /** The `Repo` is the main entry point of this library
10
11
  *
@@ -25,7 +26,10 @@ export declare class Repo extends EventEmitter<RepoEvents> {
25
26
  /** By default, we share generously with all peers. */
26
27
  /** @hidden */
27
28
  sharePolicy: SharePolicy;
28
- constructor({ storage, network, peerId, sharePolicy }: RepoConfig);
29
+ /** maps peer id to to persistance information (storageId, isEphemeral), access by collection synchronizer */
30
+ /** @hidden */
31
+ persistanceInfoByPeerId: Record<PeerId, PersistanceInfo>;
32
+ constructor({ storage, network, peerId, sharePolicy, isEphemeral, }: RepoConfig);
29
33
  /** Returns all the handles we have cached. */
30
34
  get handles(): Record<DocumentId, DocHandle<any>>;
31
35
  /** Returns a list of all connected peer ids */
@@ -62,10 +66,19 @@ export declare class Repo extends EventEmitter<RepoEvents> {
62
66
  delete(
63
67
  /** The url or documentId of the handle to delete */
64
68
  id: AnyDocumentId): void;
69
+ subscribeToRemotes: (remotes: StorageId[]) => void;
70
+ storageId: () => Promise<StorageId | undefined>;
71
+ }
72
+ interface PersistanceInfo {
73
+ storageId: StorageId;
74
+ isEphemeral: boolean;
65
75
  }
66
76
  export interface RepoConfig {
67
77
  /** Our unique identifier */
68
78
  peerId?: PeerId;
79
+ /** Indicates whether other peers should persist the sync state of this peer.
80
+ * Sync state is only persisted for non-ephemeral peers */
81
+ isEphemeral?: boolean;
69
82
  /** A storage adapter can be provided, or not */
70
83
  storage?: StorageAdapter;
71
84
  /** One or more network adapters must be provided */
@@ -100,4 +113,5 @@ export interface DocumentPayload {
100
113
  export interface DeleteDocumentPayload {
101
114
  documentId: DocumentId;
102
115
  }
116
+ export {};
103
117
  //# sourceMappingURL=Repo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAM5C,OAAO,EAAE,SAAS,EAAiC,MAAM,gBAAgB,CAAA;AAEzE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAEhE,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAGnE,8FAA8F;AAC9F;;;;;;GAMG;AACH,qBAAa,IAAK,SAAQ,YAAY,CAAC,UAAU,CAAC;;IAGhD,cAAc;IACd,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,cAAc;IACd,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IAEnC,mDAAmD;IACnD,cAAc;IACd,gBAAgB,SAAM;IAMtB,sDAAsD;IACtD,cAAc;IACd,WAAW,EAAE,WAAW,CAAmB;gBAE/B,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,UAAU;IAuIjE,8CAA8C;IAC9C,IAAI,OAAO,uCAEV;IAED,+CAA+C;IAC/C,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;IAED;;;;OAIG;IACH,MAAM,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC;IA0BzB;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC;IAuBnC;;;OAGG;IACH,IAAI,CAAC,CAAC;IACJ,sDAAsD;IACtD,EAAE,EAAE,aAAa,GAChB,SAAS,CAAC,CAAC,CAAC;IAqBf,MAAM;IACJ,oDAAoD;IACpD,EAAE,EAAE,aAAa;CAUpB;AAED,MAAM,WAAW,UAAU;IACzB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf,gDAAgD;IAChD,OAAO,CAAC,EAAE,cAAc,CAAA;IAExB,oDAAoD;IACpD,OAAO,EAAE,cAAc,EAAE,CAAA;IAEzB;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAED;;;;;;;KAOK;AACL,MAAM,MAAM,WAAW,GAAG,CACxB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,UAAU,KACpB,OAAO,CAAC,OAAO,CAAC,CAAA;AAGrB,MAAM,WAAW,UAAU;IACzB,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAA;IACxC,6BAA6B;IAC7B,iBAAiB,EAAE,CAAC,GAAG,EAAE,qBAAqB,KAAK,IAAI,CAAA;IACvD,4FAA4F;IAC5F,sBAAsB,EAAE,CAAC,GAAG,EAAE,qBAAqB,KAAK,IAAI,CAAA;CAC7D;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,CAAA;IACtB,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,UAAU,CAAA;CACvB"}
1
+ {"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"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"}
package/dist/Repo.js CHANGED
@@ -7,6 +7,8 @@ import { throttle } from "./helpers/throttle.js";
7
7
  import { NetworkSubsystem } from "./network/NetworkSubsystem.js";
8
8
  import { StorageSubsystem } from "./storage/StorageSubsystem.js";
9
9
  import { CollectionSynchronizer } from "./synchronizer/CollectionSynchronizer.js";
10
+ import { RemoteHeadsSubscriptions } from "./RemoteHeadsSubscriptions.js";
11
+ import { headsAreSame } from "./helpers/headsAreSame.js";
10
12
  /** A Repo is a collection of documents with networking, syncing, and storage capabilities. */
11
13
  /** The `Repo` is the main entry point of this library
12
14
  *
@@ -29,7 +31,11 @@ export class Repo extends EventEmitter {
29
31
  /** By default, we share generously with all peers. */
30
32
  /** @hidden */
31
33
  sharePolicy = async () => true;
32
- constructor({ storage, network, peerId, sharePolicy }) {
34
+ /** maps peer id to to persistance information (storageId, isEphemeral), access by collection synchronizer */
35
+ /** @hidden */
36
+ persistanceInfoByPeerId = {};
37
+ #remoteHeadsSubscriptions = new RemoteHeadsSubscriptions();
38
+ constructor({ storage, network, peerId, sharePolicy, isEphemeral = storage === undefined, }) {
33
39
  super();
34
40
  this.#log = debug(`automerge-repo:repo`);
35
41
  this.sharePolicy = sharePolicy ?? this.sharePolicy;
@@ -101,27 +107,119 @@ export class Repo extends EventEmitter {
101
107
  this.storageSubsystem = storageSubsystem;
102
108
  // NETWORK
103
109
  // The network subsystem deals with sending and receiving messages to and from peers.
104
- const networkSubsystem = new NetworkSubsystem(network, peerId);
110
+ const networkSubsystem = new NetworkSubsystem(network, peerId, storageSubsystem?.id() ?? Promise.resolve(undefined), isEphemeral);
105
111
  this.networkSubsystem = networkSubsystem;
106
112
  // When we get a new peer, register it with the synchronizer
107
- networkSubsystem.on("peer", async ({ peerId }) => {
113
+ networkSubsystem.on("peer", async ({ peerId, storageId, isEphemeral }) => {
108
114
  this.#log("peer connected", { peerId });
115
+ if (storageId) {
116
+ this.persistanceInfoByPeerId[peerId] = {
117
+ storageId,
118
+ isEphemeral,
119
+ };
120
+ }
121
+ this.sharePolicy(peerId)
122
+ .then(shouldShare => {
123
+ if (shouldShare) {
124
+ this.#remoteHeadsSubscriptions.addGenerousPeer(peerId);
125
+ }
126
+ })
127
+ .catch(err => {
128
+ console.log("error in share policy", { err });
129
+ });
109
130
  this.#synchronizer.addPeer(peerId);
110
131
  });
111
132
  // When a peer disconnects, remove it from the synchronizer
112
133
  networkSubsystem.on("peer-disconnected", ({ peerId }) => {
113
134
  this.#synchronizer.removePeer(peerId);
135
+ this.#remoteHeadsSubscriptions.removePeer(peerId);
114
136
  });
115
137
  // Handle incoming messages
116
138
  networkSubsystem.on("message", async (msg) => {
117
- await this.#synchronizer.receiveMessage(msg);
139
+ this.#receiveMessage(msg);
140
+ });
141
+ this.#synchronizer.on("sync-state", message => {
142
+ this.#saveSyncState(message);
143
+ const handle = this.#handleCache[message.documentId];
144
+ const info = this.persistanceInfoByPeerId[message.peerId];
145
+ if (!info) {
146
+ return;
147
+ }
148
+ const { storageId } = info;
149
+ const heads = handle.getRemoteHeads(storageId);
150
+ const haveHeadsChanged = message.syncState.theirHeads &&
151
+ (!heads || !headsAreSame(heads, message.syncState.theirHeads));
152
+ if (haveHeadsChanged) {
153
+ handle.setRemoteHeads(storageId, message.syncState.theirHeads);
154
+ if (storageId) {
155
+ this.#remoteHeadsSubscriptions.handleImmediateRemoteHeadsChanged(message.documentId, storageId, message.syncState.theirHeads);
156
+ }
157
+ }
118
158
  });
119
- if (storageSubsystem) {
120
- const debouncedSaveSyncState = throttle(({ documentId, peerId, syncState }) => {
121
- storageSubsystem.saveSyncState(documentId, peerId, syncState);
159
+ this.#remoteHeadsSubscriptions.on("notify-remote-heads", message => {
160
+ this.networkSubsystem.send({
161
+ type: "remote-heads-changed",
162
+ targetId: message.targetId,
163
+ documentId: message.documentId,
164
+ newHeads: {
165
+ [message.storageId]: {
166
+ heads: message.heads,
167
+ timestamp: message.timestamp,
168
+ },
169
+ },
170
+ });
171
+ });
172
+ this.#remoteHeadsSubscriptions.on("change-remote-subs", message => {
173
+ this.#log("change-remote-subs", message);
174
+ for (const peer of message.peers) {
175
+ this.networkSubsystem.send({
176
+ type: "remote-subscription-change",
177
+ targetId: peer,
178
+ add: message.add,
179
+ remove: message.remove,
180
+ });
181
+ }
182
+ });
183
+ this.#remoteHeadsSubscriptions.on("remote-heads-changed", message => {
184
+ const handle = this.#handleCache[message.documentId];
185
+ handle.setRemoteHeads(message.storageId, message.remoteHeads);
186
+ });
187
+ }
188
+ #receiveMessage(message) {
189
+ switch (message.type) {
190
+ case "remote-subscription-change":
191
+ this.#remoteHeadsSubscriptions.handleControlMessage(message);
192
+ break;
193
+ case "remote-heads-changed":
194
+ this.#remoteHeadsSubscriptions.handleRemoteHeads(message);
195
+ break;
196
+ case "sync":
197
+ case "request":
198
+ case "ephemeral":
199
+ case "doc-unavailable":
200
+ this.#synchronizer.receiveMessage(message).catch(err => {
201
+ console.log("error receiving message", { err });
202
+ });
203
+ }
204
+ }
205
+ #throttledSaveSyncStateHandlers = {};
206
+ /** saves sync state throttled per storage id, if a peer doesn't have a storage id it's sync state is not persisted */
207
+ #saveSyncState(message) {
208
+ if (!this.storageSubsystem) {
209
+ return;
210
+ }
211
+ const persistanceInfo = this.persistanceInfoByPeerId[message.peerId];
212
+ if (!persistanceInfo || persistanceInfo.isEphemeral) {
213
+ return;
214
+ }
215
+ const { storageId } = persistanceInfo;
216
+ let handler = this.#throttledSaveSyncStateHandlers[storageId];
217
+ if (!handler) {
218
+ handler = this.#throttledSaveSyncStateHandlers[storageId] = throttle(({ documentId, syncState }) => {
219
+ this.storageSubsystem.saveSyncState(documentId, storageId, syncState);
122
220
  }, this.saveDebounceRate);
123
- this.#synchronizer.on("sync-state", debouncedSaveSyncState);
124
221
  }
222
+ handler(message);
125
223
  }
126
224
  /** Returns an existing handle if we have it; creates one otherwise. */
127
225
  #getHandle(
@@ -237,4 +335,16 @@ export class Repo extends EventEmitter {
237
335
  delete this.#handleCache[documentId];
238
336
  this.emit("delete-document", { documentId });
239
337
  }
338
+ subscribeToRemotes = (remotes) => {
339
+ this.#log("subscribeToRemotes", { remotes });
340
+ this.#remoteHeadsSubscriptions.subscribeToRemotes(remotes);
341
+ };
342
+ storageId = async () => {
343
+ if (!this.storageSubsystem) {
344
+ return undefined;
345
+ }
346
+ else {
347
+ return this.storageSubsystem.id();
348
+ }
349
+ };
240
350
  }