@automerge/automerge-repo 2.3.0 → 2.4.0-alpha.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.
package/dist/Repo.d.ts CHANGED
@@ -36,17 +36,23 @@ export declare class Repo extends EventEmitter<RepoEvents> {
36
36
  storageSubsystem?: StorageSubsystem;
37
37
  /** @hidden */
38
38
  synchronizer: CollectionSynchronizer;
39
- /** By default, we share generously with all peers. */
40
- /** @hidden */
41
- sharePolicy: SharePolicy;
42
39
  /** maps peer id to to persistence information (storageId, isEphemeral), access by collection synchronizer */
43
40
  /** @hidden */
44
41
  peerMetadataByPeerId: Record<PeerId, PeerMetadata>;
45
- constructor({ storage, network, peerId, sharePolicy, isEphemeral, enableRemoteHeadsGossiping, denylist, saveDebounceRate, }?: RepoConfig);
42
+ constructor({ storage, network, peerId, sharePolicy, shareConfig, isEphemeral, enableRemoteHeadsGossiping, denylist, saveDebounceRate, }?: RepoConfig);
46
43
  /** Returns all the handles we have cached. */
47
44
  get handles(): Record<DocumentId, DocHandle<any>>;
48
45
  /** Returns a list of all connected peer ids */
49
46
  get peers(): PeerId[];
47
+ get peerId(): PeerId;
48
+ /** @hidden */
49
+ get sharePolicy(): SharePolicy;
50
+ /** @hidden */
51
+ set sharePolicy(policy: SharePolicy);
52
+ /** @hidden */
53
+ get shareConfig(): ShareConfig;
54
+ /** @hidden */
55
+ set shareConfig(config: ShareConfig);
50
56
  getStorageIdOfPeer(peerId: PeerId): StorageId | undefined;
51
57
  /**
52
58
  * Creates a new document and returns a handle to it. The initial value of the document is an
@@ -143,8 +149,15 @@ export interface RepoConfig {
143
149
  /**
144
150
  * Normal peers typically share generously with everyone (meaning we sync all our documents with
145
151
  * all peers). A server only syncs documents that a peer explicitly requests by ID.
152
+ * @deprecated Use `shareConfig` instead
146
153
  */
147
154
  sharePolicy?: SharePolicy;
155
+ /**
156
+ * Whether to share documents with other peers. By default we announce new
157
+ * documents to everyone and allow everyone access to documents, see the
158
+ * documentation for {@link ShareConfig} to override this
159
+ */
160
+ shareConfig?: ShareConfig;
148
161
  /**
149
162
  * Whether to enable the experimental remote heads gossiping feature
150
163
  */
@@ -169,6 +182,26 @@ export interface RepoConfig {
169
182
  * document with the peer given by `peerId`.
170
183
  * */
171
184
  export type SharePolicy = (peerId: PeerId, documentId?: DocumentId) => Promise<boolean>;
185
+ /**
186
+ * A type which determines whether we should share a document with a peer
187
+ * */
188
+ export type ShareConfig = {
189
+ /**
190
+ * Whether we should actively announce a document to a peer
191
+
192
+ * @remarks
193
+ * This functions is called after checking the `access` policy to determine
194
+ * whether we should announce a document to a connected peer. For example, a
195
+ * tab connected to a sync server might want to announce every document to the
196
+ * sync server, but the sync server would not want to announce every document
197
+ * to every connected peer
198
+ */
199
+ announce: SharePolicy;
200
+ /**
201
+ * Whether a peer should have access to the document
202
+ */
203
+ access: (peer: PeerId, doc: DocumentId) => Promise<boolean>;
204
+ };
172
205
  export interface RepoEvents {
173
206
  /** A new document was created or discovered */
174
207
  document: (arg: DocumentPayload) => void;
@@ -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
@@ -34,9 +34,10 @@ export class Repo extends EventEmitter {
34
34
  #handleCache = {};
35
35
  /** @hidden */
36
36
  synchronizer;
37
- /** By default, we share generously with all peers. */
38
- /** @hidden */
39
- sharePolicy = async () => true;
37
+ #shareConfig = {
38
+ announce: async () => true,
39
+ access: async () => true,
40
+ };
40
41
  /** maps peer id to to persistence information (storageId, isEphemeral), access by collection synchronizer */
41
42
  /** @hidden */
42
43
  peerMetadataByPeerId = {};
@@ -44,11 +45,23 @@ export class Repo extends EventEmitter {
44
45
  #remoteHeadsGossipingEnabled = false;
45
46
  #progressCache = {};
46
47
  #saveFns = {};
47
- constructor({ storage, network = [], peerId = randomPeerId(), sharePolicy, isEphemeral = storage === undefined, enableRemoteHeadsGossiping = false, denylist = [], saveDebounceRate = 100, } = {}) {
48
+ constructor({ storage, network = [], peerId = randomPeerId(), sharePolicy, shareConfig, isEphemeral = storage === undefined, enableRemoteHeadsGossiping = false, denylist = [], saveDebounceRate = 100, } = {}) {
48
49
  super();
49
50
  this.#remoteHeadsGossipingEnabled = enableRemoteHeadsGossiping;
50
51
  this.#log = debug(`automerge-repo:repo`);
51
- this.sharePolicy = sharePolicy ?? this.sharePolicy;
52
+ // Handle legacy sharePolicy
53
+ if (sharePolicy != null && shareConfig != null) {
54
+ throw new Error("cannot provide both sharePolicy and shareConfig at once");
55
+ }
56
+ if (sharePolicy) {
57
+ this.#shareConfig = {
58
+ announce: sharePolicy,
59
+ access: async () => true,
60
+ };
61
+ }
62
+ if (shareConfig) {
63
+ this.#shareConfig = shareConfig;
64
+ }
52
65
  this.on("delete-document", ({ documentId }) => {
53
66
  this.synchronizer.removeDocument(documentId);
54
67
  if (storageSubsystem) {
@@ -85,7 +98,7 @@ export class Repo extends EventEmitter {
85
98
  this.#saveFn = ({ handle, doc }) => {
86
99
  let fn = this.#saveFns[handle.documentId];
87
100
  if (!fn) {
88
- fn = throttle(() => {
101
+ fn = throttle(({ doc, handle }) => {
89
102
  void this.storageSubsystem.saveDoc(handle.documentId, doc);
90
103
  }, this.#saveDebounceRate);
91
104
  this.#saveFns[handle.documentId] = fn;
@@ -110,7 +123,8 @@ export class Repo extends EventEmitter {
110
123
  if (peerMetadata) {
111
124
  this.peerMetadataByPeerId[peerId] = { ...peerMetadata };
112
125
  }
113
- this.sharePolicy(peerId)
126
+ this.#shareConfig
127
+ .announce(peerId)
114
128
  .then(shouldShare => {
115
129
  if (shouldShare && this.#remoteHeadsGossipingEnabled) {
116
130
  this.#remoteHeadsSubscriptions.addGenerousPeer(peerId);
@@ -258,6 +272,25 @@ export class Repo extends EventEmitter {
258
272
  get peers() {
259
273
  return this.synchronizer.peers;
260
274
  }
275
+ get peerId() {
276
+ return this.networkSubsystem.peerId;
277
+ }
278
+ /** @hidden */
279
+ get sharePolicy() {
280
+ return this.#shareConfig.announce;
281
+ }
282
+ /** @hidden */
283
+ set sharePolicy(policy) {
284
+ this.#shareConfig.announce = policy;
285
+ }
286
+ /** @hidden */
287
+ get shareConfig() {
288
+ return this.#shareConfig;
289
+ }
290
+ /** @hidden */
291
+ set shareConfig(config) {
292
+ this.#shareConfig = config;
293
+ }
261
294
  getStorageIdOfPeer(peerId) {
262
295
  return this.peerMetadataByPeerId[peerId]?.storageId;
263
296
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DummyNetworkAdapter.d.ts","sourceRoot":"","sources":["../../src/helpers/DummyNetworkAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEpE,qBAAa,mBAAoB,SAAQ,cAAc;;IASrD,OAAO;IAIP,SAAS;IAYT,UAAU;gBAIE,IAAI,GAAE,OAA8B;IAQhD,OAAO,CAAC,MAAM,EAAE,MAAM;IAItB,UAAU;IAEV,aAAa,CAAC,MAAM,EAAE,MAAM;IAInB,IAAI,CAAC,OAAO,EAAE,OAAO;IAI9B,OAAO,CAAC,OAAO,EAAE,OAAO;IAIxB,MAAM,CAAC,mBAAmB,CAAC,EAAE,OAAY,EAAE,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO;CAcvE;AAED,KAAK,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;AAE/C,KAAK,OAAO,GAAG;IACb,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,aAAa,CAAA;CAC5B,CAAA"}
1
+ {"version":3,"file":"DummyNetworkAdapter.d.ts","sourceRoot":"","sources":["../../src/helpers/DummyNetworkAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEpE,qBAAa,mBAAoB,SAAQ,cAAc;;IAUrD,OAAO;IAIP,SAAS;IAYT,UAAU;gBAIE,IAAI,GAAE,OAA8B;IAQhD,OAAO,CAAC,MAAM,EAAE,MAAM;IAKtB,UAAU;IAIV,aAAa,CAAC,MAAM,EAAE,MAAM;IAInB,IAAI,CAAC,OAAO,EAAE,OAAO;IAO9B,OAAO,CAAC,OAAO,EAAE,OAAO;IAOxB,MAAM,CAAC,mBAAmB,CAAC,EAAE,OAAY,EAAE,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO;CAcvE;AAED,KAAK,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;AAE/C,KAAK,OAAO,GAAG;IACb,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,aAAa,CAAA;CAC5B,CAAA"}
@@ -2,6 +2,7 @@ import { pause } from "../../src/helpers/pause.js";
2
2
  import { NetworkAdapter } from "../../src/index.js";
3
3
  export class DummyNetworkAdapter extends NetworkAdapter {
4
4
  #sendMessage;
5
+ #connected = false;
5
6
  #ready = false;
6
7
  #readyResolver;
7
8
  #readyPromise = new Promise(resolve => {
@@ -31,16 +32,25 @@ export class DummyNetworkAdapter extends NetworkAdapter {
31
32
  this.#sendMessage = opts.sendMessage;
32
33
  }
33
34
  connect(peerId) {
35
+ this.#connected = true;
34
36
  this.peerId = peerId;
35
37
  }
36
- disconnect() { }
38
+ disconnect() {
39
+ this.#connected = false;
40
+ }
37
41
  peerCandidate(peerId) {
38
42
  this.emit("peer-candidate", { peerId, peerMetadata: {} });
39
43
  }
40
44
  send(message) {
45
+ if (!this.#connected) {
46
+ return;
47
+ }
41
48
  this.#sendMessage?.(message);
42
49
  }
43
50
  receive(message) {
51
+ if (!this.#connected) {
52
+ return;
53
+ }
44
54
  this.emit("message", message);
45
55
  }
46
56
  static createConnectedPair({ latency = 10 } = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"CollectionSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/CollectionSynchronizer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIhD,4FAA4F;AAC5F,qBAAa,sBAAuB,SAAQ,YAAY;;IAa1C,OAAO,CAAC,IAAI;IATxB,kDAAkD;IAClD,cAAc;IACd,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,CAAK;gBAOtC,IAAI,EAAE,IAAI,EAAE,QAAQ,GAAE,YAAY,EAAO;IAwD7D;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU;IAyCxC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC;IAWtC,sDAAsD;IACtD,cAAc,CAAC,UAAU,EAAE,UAAU;IAUrC,2DAA2D;IAC3D,OAAO,CAAC,MAAM,EAAE,MAAM;IAgBtB,uDAAuD;IACvD,UAAU,CAAC,MAAM,EAAE,MAAM;IASzB,+CAA+C;IAC/C,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;IAED,OAAO,IAAI;QACT,CAAC,GAAG,EAAE,MAAM,GAAG;YACb,KAAK,EAAE,MAAM,EAAE,CAAA;YACf,IAAI,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,UAAU,EAAE,MAAM,CAAA;aAAE,CAAA;SAC7C,CAAA;KACF;CASF"}
1
+ {"version":3,"file":"CollectionSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/CollectionSynchronizer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIhD,4FAA4F;AAC5F,qBAAa,sBAAuB,SAAQ,YAAY;;IAa1C,OAAO,CAAC,IAAI;IATxB,kDAAkD;IAClD,cAAc;IACd,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,CAAK;gBAOtC,IAAI,EAAE,IAAI,EAAE,QAAQ,GAAE,YAAY,EAAO;IAwD7D;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU;IAuDxC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC;IAWtC,sDAAsD;IACtD,cAAc,CAAC,UAAU,EAAE,UAAU;IAUrC,2DAA2D;IAC3D,OAAO,CAAC,MAAM,EAAE,MAAM;IAgBtB,uDAAuD;IACvD,UAAU,CAAC,MAAM,EAAE,MAAM;IASzB,+CAA+C;IAC/C,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;IAED,OAAO,IAAI;QACT,CAAC,GAAG,EAAE,MAAM,GAAG;YACb,KAAK,EAAE,MAAM,EAAE,CAAA;YACf,IAAI,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,UAAU,EAAE,MAAM,CAAA;aAAE,CAAA;SAC7C,CAAA;KACF;CAiBF"}
@@ -54,7 +54,7 @@ export class CollectionSynchronizer extends Synchronizer {
54
54
  const peers = Array.from(this.#peers);
55
55
  const generousPeers = [];
56
56
  for (const peerId of peers) {
57
- const okToShare = await this.repo.sharePolicy(peerId, documentId);
57
+ const okToShare = await this.#shouldShare(peerId, documentId);
58
58
  if (okToShare)
59
59
  generousPeers.push(peerId);
60
60
  }
@@ -83,6 +83,16 @@ export class CollectionSynchronizer extends Synchronizer {
83
83
  });
84
84
  return;
85
85
  }
86
+ const hasAccess = await this.repo.shareConfig.access(message.senderId, documentId);
87
+ if (!hasAccess) {
88
+ log("access denied");
89
+ this.emit("message", {
90
+ type: "doc-unavailable",
91
+ documentId,
92
+ targetId: message.senderId,
93
+ });
94
+ return;
95
+ }
86
96
  this.#docSetUp[documentId] = true;
87
97
  const handle = await this.repo.find(documentId, {
88
98
  allowableStates: ["ready", "unavailable", "requesting"],
@@ -125,7 +135,7 @@ export class CollectionSynchronizer extends Synchronizer {
125
135
  this.#peers.add(peerId);
126
136
  for (const docSynchronizer of Object.values(this.docSynchronizers)) {
127
137
  const { documentId } = docSynchronizer;
128
- void this.repo.sharePolicy(peerId, documentId).then(okToShare => {
138
+ void this.#shouldShare(peerId, documentId).then(okToShare => {
129
139
  if (okToShare)
130
140
  void docSynchronizer.beginSync([peerId]);
131
141
  });
@@ -148,4 +158,11 @@ export class CollectionSynchronizer extends Synchronizer {
148
158
  return [documentId, synchronizer.metrics()];
149
159
  }));
150
160
  }
161
+ async #shouldShare(peerId, documentId) {
162
+ const [announce, access] = await Promise.all([
163
+ this.repo.shareConfig.announce(peerId, documentId),
164
+ this.repo.shareConfig.access(peerId, documentId),
165
+ ]);
166
+ return announce && access;
167
+ }
151
168
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automerge/automerge-repo",
3
- "version": "2.3.0",
3
+ "version": "2.4.0-alpha.0",
4
4
  "description": "A repository object to manage a collection of automerge documents",
5
5
  "repository": "https://github.com/automerge/automerge-repo/tree/master/packages/automerge-repo",
6
6
  "author": "Peter van Hardenberg <pvh@pvh.ca>",
@@ -59,5 +59,5 @@
59
59
  "publishConfig": {
60
60
  "access": "public"
61
61
  },
62
- "gitHead": "024b98b64a6add14dca52219e92acdfdb5bed230"
62
+ "gitHead": "d6d7dfd5c4af335ff3db876db053733efada83df"
63
63
  }
package/src/Repo.ts CHANGED
@@ -85,9 +85,10 @@ export class Repo extends EventEmitter<RepoEvents> {
85
85
  /** @hidden */
86
86
  synchronizer: CollectionSynchronizer
87
87
 
88
- /** By default, we share generously with all peers. */
89
- /** @hidden */
90
- sharePolicy: SharePolicy = async () => true
88
+ #shareConfig: ShareConfig = {
89
+ announce: async () => true,
90
+ access: async () => true,
91
+ }
91
92
 
92
93
  /** maps peer id to to persistence information (storageId, isEphemeral), access by collection synchronizer */
93
94
  /** @hidden */
@@ -106,6 +107,7 @@ export class Repo extends EventEmitter<RepoEvents> {
106
107
  network = [],
107
108
  peerId = randomPeerId(),
108
109
  sharePolicy,
110
+ shareConfig,
109
111
  isEphemeral = storage === undefined,
110
112
  enableRemoteHeadsGossiping = false,
111
113
  denylist = [],
@@ -114,7 +116,20 @@ export class Repo extends EventEmitter<RepoEvents> {
114
116
  super()
115
117
  this.#remoteHeadsGossipingEnabled = enableRemoteHeadsGossiping
116
118
  this.#log = debug(`automerge-repo:repo`)
117
- this.sharePolicy = sharePolicy ?? this.sharePolicy
119
+
120
+ // Handle legacy sharePolicy
121
+ if (sharePolicy != null && shareConfig != null) {
122
+ throw new Error("cannot provide both sharePolicy and shareConfig at once")
123
+ }
124
+ if (sharePolicy) {
125
+ this.#shareConfig = {
126
+ announce: sharePolicy,
127
+ access: async () => true,
128
+ }
129
+ }
130
+ if (shareConfig) {
131
+ this.#shareConfig = shareConfig
132
+ }
118
133
 
119
134
  this.on("delete-document", ({ documentId }) => {
120
135
  this.synchronizer.removeDocument(documentId)
@@ -163,9 +178,12 @@ export class Repo extends EventEmitter<RepoEvents> {
163
178
  this.#saveFn = ({ handle, doc }: DocHandleEncodedChangePayload<any>) => {
164
179
  let fn = this.#saveFns[handle.documentId]
165
180
  if (!fn) {
166
- fn = throttle(() => {
167
- void this.storageSubsystem!.saveDoc(handle.documentId, doc)
168
- }, this.#saveDebounceRate)
181
+ fn = throttle(
182
+ ({ doc, handle }: DocHandleEncodedChangePayload<any>) => {
183
+ void this.storageSubsystem!.saveDoc(handle.documentId, doc)
184
+ },
185
+ this.#saveDebounceRate
186
+ )
169
187
  this.#saveFns[handle.documentId] = fn
170
188
  }
171
189
  fn({ handle, doc })
@@ -197,7 +215,8 @@ export class Repo extends EventEmitter<RepoEvents> {
197
215
  this.peerMetadataByPeerId[peerId] = { ...peerMetadata }
198
216
  }
199
217
 
200
- this.sharePolicy(peerId)
218
+ this.#shareConfig
219
+ .announce(peerId)
201
220
  .then(shouldShare => {
202
221
  if (shouldShare && this.#remoteHeadsGossipingEnabled) {
203
222
  this.#remoteHeadsSubscriptions.addGenerousPeer(peerId)
@@ -393,6 +412,30 @@ export class Repo extends EventEmitter<RepoEvents> {
393
412
  return this.synchronizer.peers
394
413
  }
395
414
 
415
+ get peerId(): PeerId {
416
+ return this.networkSubsystem.peerId
417
+ }
418
+
419
+ /** @hidden */
420
+ get sharePolicy(): SharePolicy {
421
+ return this.#shareConfig.announce
422
+ }
423
+
424
+ /** @hidden */
425
+ set sharePolicy(policy: SharePolicy) {
426
+ this.#shareConfig.announce = policy
427
+ }
428
+
429
+ /** @hidden */
430
+ get shareConfig(): ShareConfig {
431
+ return this.#shareConfig
432
+ }
433
+
434
+ /** @hidden */
435
+ set shareConfig(config: ShareConfig) {
436
+ this.#shareConfig = config
437
+ }
438
+
396
439
  getStorageIdOfPeer(peerId: PeerId): StorageId | undefined {
397
440
  return this.peerMetadataByPeerId[peerId]?.storageId
398
441
  }
@@ -894,9 +937,17 @@ export interface RepoConfig {
894
937
  /**
895
938
  * Normal peers typically share generously with everyone (meaning we sync all our documents with
896
939
  * all peers). A server only syncs documents that a peer explicitly requests by ID.
940
+ * @deprecated Use `shareConfig` instead
897
941
  */
898
942
  sharePolicy?: SharePolicy
899
943
 
944
+ /**
945
+ * Whether to share documents with other peers. By default we announce new
946
+ * documents to everyone and allow everyone access to documents, see the
947
+ * documentation for {@link ShareConfig} to override this
948
+ */
949
+ shareConfig?: ShareConfig
950
+
900
951
  /**
901
952
  * Whether to enable the experimental remote heads gossiping feature
902
953
  */
@@ -928,6 +979,27 @@ export type SharePolicy = (
928
979
  documentId?: DocumentId
929
980
  ) => Promise<boolean>
930
981
 
982
+ /**
983
+ * A type which determines whether we should share a document with a peer
984
+ * */
985
+ export type ShareConfig = {
986
+ /**
987
+ * Whether we should actively announce a document to a peer
988
+
989
+ * @remarks
990
+ * This functions is called after checking the `access` policy to determine
991
+ * whether we should announce a document to a connected peer. For example, a
992
+ * tab connected to a sync server might want to announce every document to the
993
+ * sync server, but the sync server would not want to announce every document
994
+ * to every connected peer
995
+ */
996
+ announce: SharePolicy
997
+ /**
998
+ * Whether a peer should have access to the document
999
+ */
1000
+ access: (peer: PeerId, doc: DocumentId) => Promise<boolean>
1001
+ }
1002
+
931
1003
  // events & payloads
932
1004
  export interface RepoEvents {
933
1005
  /** A new document was created or discovered */
@@ -4,6 +4,7 @@ import { Message, NetworkAdapter, PeerId } from "../../src/index.js"
4
4
  export class DummyNetworkAdapter extends NetworkAdapter {
5
5
  #sendMessage?: SendMessageFn
6
6
 
7
+ #connected = false
7
8
  #ready = false
8
9
  #readyResolver?: () => void
9
10
  #readyPromise: Promise<void> = new Promise<void>(resolve => {
@@ -39,20 +40,29 @@ export class DummyNetworkAdapter extends NetworkAdapter {
39
40
  }
40
41
 
41
42
  connect(peerId: PeerId) {
43
+ this.#connected = true
42
44
  this.peerId = peerId
43
45
  }
44
46
 
45
- disconnect() {}
47
+ disconnect() {
48
+ this.#connected = false
49
+ }
46
50
 
47
51
  peerCandidate(peerId: PeerId) {
48
52
  this.emit("peer-candidate", { peerId, peerMetadata: {} })
49
53
  }
50
54
 
51
55
  override send(message: Message) {
56
+ if (!this.#connected) {
57
+ return
58
+ }
52
59
  this.#sendMessage?.(message)
53
60
  }
54
61
 
55
62
  receive(message: Message) {
63
+ if (!this.#connected) {
64
+ return
65
+ }
56
66
  this.emit("message", message)
57
67
  }
58
68
 
@@ -71,7 +71,7 @@ export class CollectionSynchronizer extends Synchronizer {
71
71
  const peers = Array.from(this.#peers)
72
72
  const generousPeers: PeerId[] = []
73
73
  for (const peerId of peers) {
74
- const okToShare = await this.repo.sharePolicy(peerId, documentId)
74
+ const okToShare = await this.#shouldShare(peerId, documentId)
75
75
  if (okToShare) generousPeers.push(peerId)
76
76
  }
77
77
  return generousPeers
@@ -108,6 +108,20 @@ export class CollectionSynchronizer extends Synchronizer {
108
108
  return
109
109
  }
110
110
 
111
+ const hasAccess = await this.repo.shareConfig.access(
112
+ message.senderId,
113
+ documentId
114
+ )
115
+ if (!hasAccess) {
116
+ log("access denied")
117
+ this.emit("message", {
118
+ type: "doc-unavailable",
119
+ documentId,
120
+ targetId: message.senderId,
121
+ })
122
+ return
123
+ }
124
+
111
125
  this.#docSetUp[documentId] = true
112
126
 
113
127
  const handle = await this.repo.find(documentId, {
@@ -160,7 +174,7 @@ export class CollectionSynchronizer extends Synchronizer {
160
174
  this.#peers.add(peerId)
161
175
  for (const docSynchronizer of Object.values(this.docSynchronizers)) {
162
176
  const { documentId } = docSynchronizer
163
- void this.repo.sharePolicy(peerId, documentId).then(okToShare => {
177
+ void this.#shouldShare(peerId, documentId).then(okToShare => {
164
178
  if (okToShare) void docSynchronizer.beginSync([peerId])
165
179
  })
166
180
  }
@@ -195,4 +209,12 @@ export class CollectionSynchronizer extends Synchronizer {
195
209
  )
196
210
  )
197
211
  }
212
+
213
+ async #shouldShare(peerId: PeerId, documentId: DocumentId): Promise<boolean> {
214
+ const [announce, access] = await Promise.all([
215
+ this.repo.shareConfig.announce(peerId, documentId),
216
+ this.repo.shareConfig.access(peerId, documentId),
217
+ ])
218
+ return announce && access
219
+ }
198
220
  }
package/test/Repo.test.ts CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  generateAutomergeUrl,
12
12
  stringifyAutomergeUrl,
13
13
  } from "../src/AutomergeUrl.js"
14
- import { Repo } from "../src/Repo.js"
14
+ import { FindProgressWithMethods, Repo, ShareConfig } from "../src/Repo.js"
15
15
  import { eventPromise } from "../src/helpers/eventPromise.js"
16
16
  import { pause } from "../src/helpers/pause.js"
17
17
  import {
@@ -33,6 +33,7 @@ import {
33
33
  import { getRandomItem } from "./helpers/getRandomItem.js"
34
34
  import { TestDoc } from "./types.js"
35
35
  import { StorageId, StorageKey } from "../src/storage/types.js"
36
+ import { FindProgress } from "../src/FindProgress.js"
36
37
 
37
38
  describe("Repo", () => {
38
39
  describe("constructor", () => {
@@ -1709,6 +1710,174 @@ describe("Repo", () => {
1709
1710
  assert.deepEqual(openDocs, 0)
1710
1711
  })
1711
1712
  })
1713
+
1714
+ describe("the sharePolicy", () => {
1715
+ async function connect(left: Repo, right: Repo) {
1716
+ const [leftToRight, rightToLeft] =
1717
+ DummyNetworkAdapter.createConnectedPair({ latency: 0 })
1718
+ left.networkSubsystem.addNetworkAdapter(leftToRight)
1719
+ right.networkSubsystem.addNetworkAdapter(rightToLeft)
1720
+ leftToRight.peerCandidate(right.peerId)
1721
+ rightToLeft.peerCandidate(left.peerId)
1722
+ await Promise.all([
1723
+ left.networkSubsystem.whenReady(),
1724
+ right.networkSubsystem.whenReady(),
1725
+ ])
1726
+ await pause(10)
1727
+ }
1728
+
1729
+ async function withTimeout<T>(
1730
+ promise: Promise<T>,
1731
+ timeout: number
1732
+ ): Promise<T | undefined> {
1733
+ const timeoutPromise = new Promise<T | undefined>(resolve => {
1734
+ setTimeout(() => resolve(undefined), timeout)
1735
+ })
1736
+ return Promise.race([promise, timeoutPromise])
1737
+ }
1738
+
1739
+ async function awaitState(
1740
+ progress: FindProgress<unknown> | FindProgressWithMethods<unknown>,
1741
+ state: string
1742
+ ): Promise<void> {
1743
+ if (progress.state == state) {
1744
+ return
1745
+ }
1746
+ if (!("subscribe" in progress)) {
1747
+ throw new Error(
1748
+ `expected progress in state ${state} but was in final state ${progress.state}`
1749
+ )
1750
+ }
1751
+ await new Promise(resolve => {
1752
+ const unsubscribe = progress.subscribe(progress => {
1753
+ if (progress.state === state) {
1754
+ unsubscribe()
1755
+ resolve(null)
1756
+ }
1757
+ })
1758
+ })
1759
+ }
1760
+
1761
+ // The parts of `RepoConfig` which are either the old sharePolicy API or the new shareConfig API
1762
+ type EitherConfig = { sharePolicy?: SharePolicy; shareConfig?: ShareConfig }
1763
+
1764
+ /// Create two connected peers with the given share configurations
1765
+ async function twoPeers({
1766
+ alice: aliceConfig,
1767
+ bob: bobConfig,
1768
+ }: {
1769
+ alice: EitherConfig
1770
+ bob: EitherConfig
1771
+ }): Promise<{ alice: Repo; bob: Repo }> {
1772
+ const alice = new Repo({
1773
+ peerId: "alice" as PeerId,
1774
+ ...aliceConfig,
1775
+ })
1776
+ const bob = new Repo({
1777
+ peerId: "bob" as PeerId,
1778
+ ...bobConfig,
1779
+ })
1780
+ await connect(alice, bob)
1781
+ return { alice, bob }
1782
+ }
1783
+
1784
+ describe("the legacy API", () => {
1785
+ it("should announce documents to peers for whom the sharePolicy returns true", async () => {
1786
+ const { alice, bob } = await twoPeers({
1787
+ alice: { sharePolicy: async () => true },
1788
+ bob: { sharePolicy: async () => true },
1789
+ })
1790
+ const handle = alice.create({ foo: "bar" })
1791
+
1792
+ // Wait for the announcement to be synced
1793
+ await pause(100)
1794
+
1795
+ // Disconnect and stop alice
1796
+ await alice.shutdown()
1797
+
1798
+ // Bob should have the handle already because it was announced to him
1799
+ const bobHandle = await bob.find(handle.url)
1800
+ })
1801
+
1802
+ it("should not annouce documents to peers for whom the sharePolicy returns false", async () => {
1803
+ const { alice, bob } = await twoPeers({
1804
+ alice: { sharePolicy: async () => false },
1805
+ bob: { sharePolicy: async () => true },
1806
+ })
1807
+ const handle = alice.create({ foo: "bar" })
1808
+
1809
+ // Disconnect and stop alice
1810
+ await alice.shutdown()
1811
+
1812
+ // Bob should have the handle already because it was announced to him
1813
+ const bobHandle = await withTimeout(bob.find(handle.url), 100)
1814
+ assert.equal(bobHandle, null)
1815
+ })
1816
+
1817
+ it("should respond to direct requests for document where the sharePolicy returns false", async () => {
1818
+ const { alice, bob } = await twoPeers({
1819
+ alice: { sharePolicy: async () => false },
1820
+ bob: { sharePolicy: async () => true },
1821
+ })
1822
+ await connect(alice, bob)
1823
+
1824
+ const aliceHandle = alice.create({ foo: "bar" })
1825
+ const bobHandle = await bob.find(aliceHandle.url)
1826
+ })
1827
+ })
1828
+
1829
+ it("should respond to direct requests for document where the announce policy returns false but the access policy returns true", async () => {
1830
+ const { alice, bob } = await twoPeers({
1831
+ alice: {
1832
+ shareConfig: {
1833
+ announce: async () => false,
1834
+ access: async () => true,
1835
+ },
1836
+ },
1837
+ bob: { sharePolicy: async () => true },
1838
+ })
1839
+
1840
+ const aliceHandle = alice.create({ foo: "bar" })
1841
+ const bobHandle = await bob.find(aliceHandle.url)
1842
+ })
1843
+
1844
+ it("should not respond to direct requests for a document where the access policy returns false and the announce policy return trrrue", async () => {
1845
+ const { alice, bob } = await twoPeers({
1846
+ alice: {
1847
+ shareConfig: {
1848
+ announce: async () => true,
1849
+ access: async () => false,
1850
+ },
1851
+ },
1852
+ bob: { sharePolicy: async () => true },
1853
+ })
1854
+ await connect(alice, bob)
1855
+
1856
+ const aliceHandle = alice.create({ foo: "bar" })
1857
+ withTimeout(
1858
+ awaitState(bob.findWithProgress(aliceHandle.url), "unavailable"),
1859
+ 500
1860
+ )
1861
+ })
1862
+
1863
+ it("should not respond to direct requests for a document where the access policy and the announce policy return false", async () => {
1864
+ const { alice, bob } = await twoPeers({
1865
+ alice: {
1866
+ shareConfig: {
1867
+ announce: async () => false,
1868
+ access: async () => false,
1869
+ },
1870
+ },
1871
+ bob: { sharePolicy: async () => false },
1872
+ })
1873
+
1874
+ const aliceHandle = alice.create({ foo: "bar" })
1875
+ withTimeout(
1876
+ awaitState(bob.findWithProgress(aliceHandle.url), "unavailable"),
1877
+ 500
1878
+ )
1879
+ })
1880
+ })
1712
1881
  })
1713
1882
 
1714
1883
  describe("Repo heads-in-URLs functionality", () => {