@automerge/automerge-repo 2.5.0 → 2.5.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.
package/dist/Repo.d.ts CHANGED
@@ -148,6 +148,7 @@ export declare class Repo extends EventEmitter<RepoEvents> {
148
148
  [key: string]: any;
149
149
  };
150
150
  };
151
+ shareConfigChanged(): void;
151
152
  }
152
153
  export interface RepoConfig {
153
154
  /** Our unique identifier */
@@ -1 +1 @@
1
- {"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"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"}
package/dist/Repo.js CHANGED
@@ -744,4 +744,7 @@ export class Repo extends EventEmitter {
744
744
  metrics() {
745
745
  return { documents: this.synchronizer.metrics() };
746
746
  }
747
+ shareConfigChanged() {
748
+ void this.synchronizer.reevaluateDocumentShare();
749
+ }
747
750
  }
@@ -29,6 +29,14 @@ export declare class CollectionSynchronizer extends Synchronizer {
29
29
  removePeer(peerId: PeerId): void;
30
30
  /** Returns a list of all connected peer ids */
31
31
  get peers(): PeerId[];
32
+ /**
33
+ * Re-evaluates share policy for a document and updates sync accordingly
34
+ *
35
+ * @remarks
36
+ * This is called when the share policy for a document has changed. It re-evaluates
37
+ * which peers should have access and starts/stops synchronization as needed.
38
+ */
39
+ reevaluateDocumentShare(): Promise<void>;
32
40
  metrics(): {
33
41
  [key: string]: {
34
42
  peers: PeerId[];
@@ -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;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"}
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;;IAc1C,OAAO,CAAC,IAAI;IAVxB,kDAAkD;IAClD,cAAc;IACd,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,CAAK;gBAQtC,IAAI,EAAE,IAAI,EAAE,QAAQ,GAAE,YAAY,EAAO;IAwD7D;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU;IAiExC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC;IAYtC,sDAAsD;IACtD,cAAc,CAAC,UAAU,EAAE,UAAU;IAUrC,2DAA2D;IAC3D,OAAO,CAAC,MAAM,EAAE,MAAM;IAgBtB,uDAAuD;IACvD,UAAU,CAAC,MAAM,EAAE,MAAM;IAYzB,+CAA+C;IAC/C,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;IAED;;;;;;OAMG;IACG,uBAAuB;IAoC7B,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;CAmBF"}
@@ -14,6 +14,7 @@ export class CollectionSynchronizer extends Synchronizer {
14
14
  /** Used to determine if the document is know to the Collection and a synchronizer exists or is being set up */
15
15
  #docSetUp = {};
16
16
  #denylist;
17
+ #hasRequested = new Map();
17
18
  constructor(repo, denylist = []) {
18
19
  super();
19
20
  this.repo = repo;
@@ -83,6 +84,15 @@ export class CollectionSynchronizer extends Synchronizer {
83
84
  });
84
85
  return;
85
86
  }
87
+ // Record the request so that even if access is denied now, we know that the
88
+ // peer requested the document so that if the share policy changes we know
89
+ // to begin syncing with this peer
90
+ if (message.type === "request") {
91
+ if (!this.#hasRequested.has(documentId)) {
92
+ this.#hasRequested.set(documentId, new Set());
93
+ }
94
+ this.#hasRequested.get(documentId)?.add(message.senderId);
95
+ }
86
96
  const hasAccess = await this.repo.shareConfig.access(message.senderId, documentId);
87
97
  if (!hasAccess) {
88
98
  log("access denied");
@@ -111,6 +121,7 @@ export class CollectionSynchronizer extends Synchronizer {
111
121
  if (this.#docSetUp[handle.documentId]) {
112
122
  return;
113
123
  }
124
+ this.#docSetUp[handle.documentId] = true;
114
125
  const docSynchronizer = this.#fetchDocSynchronizer(handle);
115
126
  void this.#documentGenerousPeers(handle.documentId).then(peers => {
116
127
  void docSynchronizer.beginSync(peers);
@@ -145,6 +156,9 @@ export class CollectionSynchronizer extends Synchronizer {
145
156
  removePeer(peerId) {
146
157
  log(`removing peer ${peerId}`);
147
158
  this.#peers.delete(peerId);
159
+ for (const requested of this.#hasRequested.values()) {
160
+ requested.delete(peerId);
161
+ }
148
162
  for (const docSynchronizer of Object.values(this.docSynchronizers)) {
149
163
  docSynchronizer.endSync(peerId);
150
164
  }
@@ -153,6 +167,38 @@ export class CollectionSynchronizer extends Synchronizer {
153
167
  get peers() {
154
168
  return Array.from(this.#peers);
155
169
  }
170
+ /**
171
+ * Re-evaluates share policy for a document and updates sync accordingly
172
+ *
173
+ * @remarks
174
+ * This is called when the share policy for a document has changed. It re-evaluates
175
+ * which peers should have access and starts/stops synchronization as needed.
176
+ */
177
+ async reevaluateDocumentShare() {
178
+ const peers = Array.from(this.#peers);
179
+ const docPromises = [];
180
+ for (const docSynchronizer of Object.values(this.docSynchronizers)) {
181
+ const documentId = docSynchronizer.documentId;
182
+ docPromises.push((async () => {
183
+ for (const peerId of peers) {
184
+ const shouldShare = await this.#shouldShare(peerId, documentId);
185
+ const isAlreadySyncing = docSynchronizer.hasPeer(peerId);
186
+ log(`reevaluateDocumentShare: ${peerId} for ${documentId}, shouldShare: ${shouldShare}, isAlreadySyncing: ${isAlreadySyncing}`);
187
+ if (shouldShare && !isAlreadySyncing) {
188
+ log(`reevaluateDocumentShare: starting sync with ${peerId} for ${documentId}`);
189
+ void docSynchronizer.beginSync([peerId]);
190
+ }
191
+ else if (!shouldShare && isAlreadySyncing) {
192
+ log(`reevaluateDocumentShare: stopping sync with ${peerId} for ${documentId}`);
193
+ docSynchronizer.endSync(peerId);
194
+ }
195
+ }
196
+ })().catch(e => {
197
+ console.log(`error reevaluating document share for ${documentId}: ${e}`);
198
+ }));
199
+ }
200
+ await Promise.allSettled(docPromises);
201
+ }
156
202
  metrics() {
157
203
  return Object.fromEntries(Object.entries(this.docSynchronizers).map(([documentId, synchronizer]) => {
158
204
  return [documentId, synchronizer.metrics()];
@@ -163,6 +209,7 @@ export class CollectionSynchronizer extends Synchronizer {
163
209
  this.repo.shareConfig.announce(peerId, documentId),
164
210
  this.repo.shareConfig.access(peerId, documentId),
165
211
  ]);
166
- return announce && access;
212
+ const hasRequested = this.#hasRequested.get(documentId)?.has(peerId) ?? false;
213
+ return announce || (access && hasRequested);
167
214
  }
168
215
  }
@@ -130,8 +130,8 @@ export class DocSynchronizer extends Synchronizer {
130
130
  durationMillis: end - start,
131
131
  forPeer: peerId,
132
132
  });
133
+ this.#setSyncState(peerId, newSyncState);
133
134
  if (message) {
134
- this.#setSyncState(peerId, newSyncState);
135
135
  const isNew = A.getHeads(doc).length === 0;
136
136
  if (!this.#handle.isReady() &&
137
137
  isNew &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automerge/automerge-repo",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
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": "fffb5789bfc7a18b3d9f1e668395c7c465e34be1"
62
+ "gitHead": "66b8457234d04468e653b85d5cf58a9a3cd4c691"
63
63
  }
package/src/Repo.ts CHANGED
@@ -977,6 +977,10 @@ export class Repo extends EventEmitter<RepoEvents> {
977
977
  metrics(): { documents: { [key: string]: any } } {
978
978
  return { documents: this.synchronizer.metrics() }
979
979
  }
980
+
981
+ shareConfigChanged() {
982
+ void this.synchronizer.reevaluateDocumentShare()
983
+ }
980
984
  }
981
985
 
982
986
  export interface RepoConfig {
@@ -22,6 +22,7 @@ export class CollectionSynchronizer extends Synchronizer {
22
22
  #docSetUp: Record<DocumentId, boolean> = {}
23
23
 
24
24
  #denylist: DocumentId[]
25
+ #hasRequested: Map<DocumentId, Set<PeerId>> = new Map()
25
26
 
26
27
  constructor(private repo: Repo, denylist: AutomergeUrl[] = []) {
27
28
  super()
@@ -108,6 +109,16 @@ export class CollectionSynchronizer extends Synchronizer {
108
109
  return
109
110
  }
110
111
 
112
+ // Record the request so that even if access is denied now, we know that the
113
+ // peer requested the document so that if the share policy changes we know
114
+ // to begin syncing with this peer
115
+ if (message.type === "request") {
116
+ if (!this.#hasRequested.has(documentId)) {
117
+ this.#hasRequested.set(documentId, new Set())
118
+ }
119
+ this.#hasRequested.get(documentId)?.add(message.senderId)
120
+ }
121
+
111
122
  const hasAccess = await this.repo.shareConfig.access(
112
123
  message.senderId,
113
124
  documentId
@@ -146,6 +157,7 @@ export class CollectionSynchronizer extends Synchronizer {
146
157
  if (this.#docSetUp[handle.documentId]) {
147
158
  return
148
159
  }
160
+ this.#docSetUp[handle.documentId] = true
149
161
  const docSynchronizer = this.#fetchDocSynchronizer(handle)
150
162
  void this.#documentGenerousPeers(handle.documentId).then(peers => {
151
163
  void docSynchronizer.beginSync(peers)
@@ -184,6 +196,9 @@ export class CollectionSynchronizer extends Synchronizer {
184
196
  removePeer(peerId: PeerId) {
185
197
  log(`removing peer ${peerId}`)
186
198
  this.#peers.delete(peerId)
199
+ for (const requested of this.#hasRequested.values()) {
200
+ requested.delete(peerId)
201
+ }
187
202
 
188
203
  for (const docSynchronizer of Object.values(this.docSynchronizers)) {
189
204
  docSynchronizer.endSync(peerId)
@@ -195,6 +210,49 @@ export class CollectionSynchronizer extends Synchronizer {
195
210
  return Array.from(this.#peers)
196
211
  }
197
212
 
213
+ /**
214
+ * Re-evaluates share policy for a document and updates sync accordingly
215
+ *
216
+ * @remarks
217
+ * This is called when the share policy for a document has changed. It re-evaluates
218
+ * which peers should have access and starts/stops synchronization as needed.
219
+ */
220
+ async reevaluateDocumentShare() {
221
+ const peers = Array.from(this.#peers)
222
+ const docPromises = []
223
+ for (const docSynchronizer of Object.values(this.docSynchronizers)) {
224
+ const documentId = docSynchronizer.documentId
225
+ docPromises.push(
226
+ (async () => {
227
+ for (const peerId of peers) {
228
+ const shouldShare = await this.#shouldShare(peerId, documentId)
229
+ const isAlreadySyncing = docSynchronizer.hasPeer(peerId)
230
+
231
+ log(
232
+ `reevaluateDocumentShare: ${peerId} for ${documentId}, shouldShare: ${shouldShare}, isAlreadySyncing: ${isAlreadySyncing}`
233
+ )
234
+ if (shouldShare && !isAlreadySyncing) {
235
+ log(
236
+ `reevaluateDocumentShare: starting sync with ${peerId} for ${documentId}`
237
+ )
238
+ void docSynchronizer.beginSync([peerId])
239
+ } else if (!shouldShare && isAlreadySyncing) {
240
+ log(
241
+ `reevaluateDocumentShare: stopping sync with ${peerId} for ${documentId}`
242
+ )
243
+ docSynchronizer.endSync(peerId)
244
+ }
245
+ }
246
+ })().catch(e => {
247
+ console.log(
248
+ `error reevaluating document share for ${documentId}: ${e}`
249
+ )
250
+ })
251
+ )
252
+ }
253
+ await Promise.allSettled(docPromises)
254
+ }
255
+
198
256
  metrics(): {
199
257
  [key: string]: {
200
258
  peers: PeerId[]
@@ -215,6 +273,8 @@ export class CollectionSynchronizer extends Synchronizer {
215
273
  this.repo.shareConfig.announce(peerId, documentId),
216
274
  this.repo.shareConfig.access(peerId, documentId),
217
275
  ])
218
- return announce && access
276
+ const hasRequested =
277
+ this.#hasRequested.get(documentId)?.has(peerId) ?? false
278
+ return announce || (access && hasRequested)
219
279
  }
220
280
  }
@@ -201,8 +201,8 @@ export class DocSynchronizer extends Synchronizer {
201
201
  forPeer: peerId,
202
202
  })
203
203
 
204
+ this.#setSyncState(peerId, newSyncState)
204
205
  if (message) {
205
- this.#setSyncState(peerId, newSyncState)
206
206
  const isNew = A.getHeads(doc).length === 0
207
207
 
208
208
  if (
package/test/Repo.test.ts CHANGED
@@ -35,6 +35,10 @@ import {
35
35
  LargeObject,
36
36
  generateLargeObject,
37
37
  } from "./helpers/generate-large-object.js"
38
+ import twoPeers from "./helpers/twoPeers.js"
39
+ import connectRepos from "./helpers/connectRepos.js"
40
+ import awaitState from "./helpers/awaitState.js"
41
+ import withTimeout from "./helpers/withTimeout.js"
38
42
  import { getRandomItem } from "./helpers/getRandomItem.js"
39
43
  import { TestDoc } from "./types.js"
40
44
  import { StorageId, StorageKey } from "../src/storage/types.js"
@@ -1182,6 +1186,49 @@ describe("Repo", () => {
1182
1186
  teardown()
1183
1187
  })
1184
1188
 
1189
+ it("a previously unavailable document syncs if a connected peer obtains it (but doesn't announce it)", async () => {
1190
+ const alice = new Repo({
1191
+ peerId: "alice" as PeerId,
1192
+ shareConfig: {
1193
+ announce: async peerId => peerId === "charlie",
1194
+ access: async () => true,
1195
+ },
1196
+ })
1197
+ const bob = new Repo({
1198
+ peerId: "bob" as PeerId,
1199
+ shareConfig: {
1200
+ announce: async () => true,
1201
+ access: async () => true,
1202
+ },
1203
+ })
1204
+ const charlie = new Repo({
1205
+ peerId: "charlie" as PeerId,
1206
+ shareConfig: {
1207
+ announce: async () => false,
1208
+ access: async () => true,
1209
+ },
1210
+ })
1211
+ await connectRepos(alice, bob)
1212
+
1213
+ const charlieHandle = charlie.create({ foo: "bar" })
1214
+
1215
+ // Charlie isn't connected to any peer, so we don't have the document
1216
+ await assert.rejects(bob.find(charlieHandle.url))
1217
+
1218
+ // Now, connect charlie to alice
1219
+ await connectRepos(alice, charlie)
1220
+
1221
+ // Alice should now find the document
1222
+ const aliceHandle = await withTimeout(alice.find(charlieHandle.url), 500)
1223
+ assert.deepStrictEqual(aliceHandle.doc(), { foo: "bar" })
1224
+
1225
+ await pause(150) // wait for the sync debounce rate to elapse
1226
+
1227
+ // Bob should now find the document via alice
1228
+ const bobHandle = await withTimeout(bob.find(charlieHandle.url), 500)
1229
+ assert.deepStrictEqual(bobHandle.doc(), { foo: "bar" })
1230
+ })
1231
+
1185
1232
  it("a previously unavailable document becomes available if the network adapter initially has no peers", async () => {
1186
1233
  // It is possible for a network adapter to be ready without any peer
1187
1234
  // being announced (e.g. the BroadcastChannelNetworkAdapter). In this
@@ -1716,174 +1763,6 @@ describe("Repo", () => {
1716
1763
  assert.deepEqual(openDocs, 0)
1717
1764
  })
1718
1765
  })
1719
-
1720
- describe("the sharePolicy", () => {
1721
- async function connect(left: Repo, right: Repo) {
1722
- const [leftToRight, rightToLeft] =
1723
- DummyNetworkAdapter.createConnectedPair({ latency: 0 })
1724
- left.networkSubsystem.addNetworkAdapter(leftToRight)
1725
- right.networkSubsystem.addNetworkAdapter(rightToLeft)
1726
- leftToRight.peerCandidate(right.peerId)
1727
- rightToLeft.peerCandidate(left.peerId)
1728
- await Promise.all([
1729
- left.networkSubsystem.whenReady(),
1730
- right.networkSubsystem.whenReady(),
1731
- ])
1732
- await pause(10)
1733
- }
1734
-
1735
- async function withTimeout<T>(
1736
- promise: Promise<T>,
1737
- timeout: number
1738
- ): Promise<T | undefined> {
1739
- const timeoutPromise = new Promise<T | undefined>(resolve => {
1740
- setTimeout(() => resolve(undefined), timeout)
1741
- })
1742
- return Promise.race([promise, timeoutPromise])
1743
- }
1744
-
1745
- async function awaitState(
1746
- progress: FindProgress<unknown> | FindProgressWithMethods<unknown>,
1747
- state: string
1748
- ): Promise<void> {
1749
- if (progress.state == state) {
1750
- return
1751
- }
1752
- if (!("subscribe" in progress)) {
1753
- throw new Error(
1754
- `expected progress in state ${state} but was in final state ${progress.state}`
1755
- )
1756
- }
1757
- await new Promise(resolve => {
1758
- const unsubscribe = progress.subscribe(progress => {
1759
- if (progress.state === state) {
1760
- unsubscribe()
1761
- resolve(null)
1762
- }
1763
- })
1764
- })
1765
- }
1766
-
1767
- // The parts of `RepoConfig` which are either the old sharePolicy API or the new shareConfig API
1768
- type EitherConfig = { sharePolicy?: SharePolicy; shareConfig?: ShareConfig }
1769
-
1770
- /// Create two connected peers with the given share configurations
1771
- async function twoPeers({
1772
- alice: aliceConfig,
1773
- bob: bobConfig,
1774
- }: {
1775
- alice: EitherConfig
1776
- bob: EitherConfig
1777
- }): Promise<{ alice: Repo; bob: Repo }> {
1778
- const alice = new Repo({
1779
- peerId: "alice" as PeerId,
1780
- ...aliceConfig,
1781
- })
1782
- const bob = new Repo({
1783
- peerId: "bob" as PeerId,
1784
- ...bobConfig,
1785
- })
1786
- await connect(alice, bob)
1787
- return { alice, bob }
1788
- }
1789
-
1790
- describe("the legacy API", () => {
1791
- it("should announce documents to peers for whom the sharePolicy returns true", async () => {
1792
- const { alice, bob } = await twoPeers({
1793
- alice: { sharePolicy: async () => true },
1794
- bob: { sharePolicy: async () => true },
1795
- })
1796
- const handle = alice.create({ foo: "bar" })
1797
-
1798
- // Wait for the announcement to be synced
1799
- await pause(100)
1800
-
1801
- // Disconnect and stop alice
1802
- await alice.shutdown()
1803
-
1804
- // Bob should have the handle already because it was announced to him
1805
- const bobHandle = await bob.find(handle.url)
1806
- })
1807
-
1808
- it("should not annouce documents to peers for whom the sharePolicy returns false", async () => {
1809
- const { alice, bob } = await twoPeers({
1810
- alice: { sharePolicy: async () => false },
1811
- bob: { sharePolicy: async () => true },
1812
- })
1813
- const handle = alice.create({ foo: "bar" })
1814
-
1815
- // Disconnect and stop alice
1816
- await alice.shutdown()
1817
-
1818
- // Bob should have the handle already because it was announced to him
1819
- const bobHandle = await withTimeout(bob.find(handle.url), 100)
1820
- assert.equal(bobHandle, null)
1821
- })
1822
-
1823
- it("should respond to direct requests for document where the sharePolicy returns false", async () => {
1824
- const { alice, bob } = await twoPeers({
1825
- alice: { sharePolicy: async () => false },
1826
- bob: { sharePolicy: async () => true },
1827
- })
1828
- await connect(alice, bob)
1829
-
1830
- const aliceHandle = alice.create({ foo: "bar" })
1831
- const bobHandle = await bob.find(aliceHandle.url)
1832
- })
1833
- })
1834
-
1835
- it("should respond to direct requests for document where the announce policy returns false but the access policy returns true", async () => {
1836
- const { alice, bob } = await twoPeers({
1837
- alice: {
1838
- shareConfig: {
1839
- announce: async () => false,
1840
- access: async () => true,
1841
- },
1842
- },
1843
- bob: { sharePolicy: async () => true },
1844
- })
1845
-
1846
- const aliceHandle = alice.create({ foo: "bar" })
1847
- const bobHandle = await bob.find(aliceHandle.url)
1848
- })
1849
-
1850
- it("should not respond to direct requests for a document where the access policy returns false and the announce policy return trrrue", async () => {
1851
- const { alice, bob } = await twoPeers({
1852
- alice: {
1853
- shareConfig: {
1854
- announce: async () => true,
1855
- access: async () => false,
1856
- },
1857
- },
1858
- bob: { sharePolicy: async () => true },
1859
- })
1860
- await connect(alice, bob)
1861
-
1862
- const aliceHandle = alice.create({ foo: "bar" })
1863
- withTimeout(
1864
- awaitState(bob.findWithProgress(aliceHandle.url), "unavailable"),
1865
- 500
1866
- )
1867
- })
1868
-
1869
- it("should not respond to direct requests for a document where the access policy and the announce policy return false", async () => {
1870
- const { alice, bob } = await twoPeers({
1871
- alice: {
1872
- shareConfig: {
1873
- announce: async () => false,
1874
- access: async () => false,
1875
- },
1876
- },
1877
- bob: { sharePolicy: async () => false },
1878
- })
1879
-
1880
- const aliceHandle = alice.create({ foo: "bar" })
1881
- withTimeout(
1882
- awaitState(bob.findWithProgress(aliceHandle.url), "unavailable"),
1883
- 500
1884
- )
1885
- })
1886
- })
1887
1766
  })
1888
1767
 
1889
1768
  describe("Repo heads-in-URLs functionality", () => {
@@ -0,0 +1,244 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import assert from "assert"
3
+ import twoPeers from "./helpers/twoPeers.js"
4
+ import connectRepos from "./helpers/connectRepos.js"
5
+ import awaitState from "./helpers/awaitState.js"
6
+ import withTimeout from "./helpers/withTimeout.js"
7
+ import pause from "./helpers/pause.js"
8
+ import { Repo } from "../src/Repo.js"
9
+ import { PeerId } from "../src/types.js"
10
+
11
+ describe("the sharePolicy APIs", () => {
12
+ describe("the legacy API", () => {
13
+ it("should announce documents to peers for whom the sharePolicy returns true", async () => {
14
+ const { alice, bob } = await twoPeers({
15
+ alice: { sharePolicy: async () => true },
16
+ bob: { sharePolicy: async () => true },
17
+ })
18
+ const handle = alice.create({ foo: "bar" })
19
+
20
+ // Wait for the announcement to be synced
21
+ await pause(100)
22
+
23
+ // Disconnect and stop alice
24
+ await alice.shutdown()
25
+
26
+ // Bob should have the handle already because it was announced to him
27
+ const bobHandle = await bob.find(handle.url)
28
+ })
29
+
30
+ it("should not annouce documents to peers for whom the sharePolicy returns false", async () => {
31
+ const { alice, bob } = await twoPeers({
32
+ alice: { sharePolicy: async () => false },
33
+ bob: { sharePolicy: async () => true },
34
+ })
35
+ const handle = alice.create({ foo: "bar" })
36
+
37
+ // Disconnect and stop alice
38
+ await alice.shutdown()
39
+
40
+ // Bob should have the handle already because it was announced to him
41
+ const bobHandle = await withTimeout(bob.find(handle.url), 100)
42
+ assert.equal(bobHandle, null)
43
+ })
44
+
45
+ it("should respond to direct requests for document where the sharePolicy returns false", async () => {
46
+ const { alice, bob } = await twoPeers({
47
+ alice: { sharePolicy: async () => false },
48
+ bob: { sharePolicy: async () => true },
49
+ })
50
+
51
+ const aliceHandle = alice.create({ foo: "bar" })
52
+ const bobHandle = await bob.find(aliceHandle.url)
53
+ })
54
+ })
55
+
56
+ it("should respond to direct requests for document where the announce policy returns false but the access policy returns true", async () => {
57
+ const { alice, bob } = await twoPeers({
58
+ alice: {
59
+ shareConfig: {
60
+ announce: async () => false,
61
+ access: async () => true,
62
+ },
63
+ },
64
+ bob: { sharePolicy: async () => true },
65
+ })
66
+
67
+ const aliceHandle = alice.create({ foo: "bar" })
68
+ const bobHandle = await bob.find(aliceHandle.url)
69
+ })
70
+
71
+ it("should not respond to direct requests for a document where the access policy returns false and the announce policy return trrrue", async () => {
72
+ const { alice, bob } = await twoPeers({
73
+ alice: {
74
+ shareConfig: {
75
+ announce: async () => true,
76
+ access: async () => false,
77
+ },
78
+ },
79
+ bob: { sharePolicy: async () => true },
80
+ })
81
+
82
+ const aliceHandle = alice.create({ foo: "bar" })
83
+ withTimeout(
84
+ awaitState(bob.findWithProgress(aliceHandle.url), "unavailable"),
85
+ 500
86
+ )
87
+ })
88
+
89
+ it("should not respond to direct requests for a document where the access policy and the announce policy return false", async () => {
90
+ const { alice, bob } = await twoPeers({
91
+ alice: {
92
+ shareConfig: {
93
+ announce: async () => false,
94
+ access: async () => false,
95
+ },
96
+ },
97
+ bob: { sharePolicy: async () => false },
98
+ })
99
+
100
+ const aliceHandle = alice.create({ foo: "bar" })
101
+ withTimeout(
102
+ awaitState(bob.findWithProgress(aliceHandle.url), "unavailable"),
103
+ 500
104
+ )
105
+ })
106
+
107
+ describe("Repo.sharePolicyChanged", () => {
108
+ it("should respond to requests for a dochandle which was denied by the sharepolicy but then allowed", async () => {
109
+ const alicePolicy = { shouldShare: false }
110
+ const { alice, bob } = await twoPeers({
111
+ alice: {
112
+ shareConfig: {
113
+ announce: async () => false,
114
+ access: async () => alicePolicy.shouldShare,
115
+ },
116
+ },
117
+ bob: { sharePolicy: async () => true },
118
+ })
119
+
120
+ const aliceHandle = alice.create({ foo: "bar" })
121
+ await withTimeout(
122
+ awaitState(bob.findWithProgress(aliceHandle.url), "unavailable"),
123
+ 500
124
+ )
125
+
126
+ // Change policy to allow sharing
127
+ alicePolicy.shouldShare = true
128
+ alice.shareConfigChanged()
129
+
130
+ // Give time for Alices syncDebounceRate to elapse to start syncing with Bob
131
+ await pause(150)
132
+
133
+ const bobHandle = await bob.find(aliceHandle.url)
134
+ expect(bobHandle.doc()).toEqual({ foo: "bar" })
135
+ })
136
+
137
+ it("should stop sending changes to a peer who had access but was then removed", async () => {
138
+ const alicePolicy = {
139
+ shouldShareWithBob: true,
140
+ }
141
+ const alice = new Repo({
142
+ peerId: "alice" as PeerId,
143
+ shareConfig: {
144
+ announce: async () => false,
145
+ access: async peerId => {
146
+ if (peerId === "bob") {
147
+ return alicePolicy.shouldShareWithBob
148
+ }
149
+ return true
150
+ },
151
+ },
152
+ })
153
+ const bob = new Repo({
154
+ peerId: "bob" as PeerId,
155
+ shareConfig: {
156
+ announce: async () => true,
157
+ access: async () => true,
158
+ },
159
+ })
160
+ const charlie = new Repo({
161
+ peerId: "charlie" as PeerId,
162
+ shareConfig: {
163
+ announce: async () => true,
164
+ access: async () => true,
165
+ },
166
+ })
167
+
168
+ await connectRepos(alice, charlie)
169
+ await connectRepos(alice, bob)
170
+
171
+ // create a handle on alice, request it on bob and charlie
172
+ const aliceHandle = alice.create({ foo: "bar" })
173
+ const bobHandle = await bob.find<{ foo: string }>(aliceHandle.url)
174
+ const charlieHandle = await charlie.find<{ foo: string }>(aliceHandle.url)
175
+
176
+ // Now remove bobs access
177
+ alicePolicy.shouldShareWithBob = false
178
+ alice.shareConfigChanged()
179
+
180
+ // Now make a change on charlie
181
+ charlieHandle.change(d => (d.foo = "baz"))
182
+
183
+ // Wait for sync to propagate
184
+ await pause(300)
185
+
186
+ assert.deepStrictEqual(bobHandle.doc(), { foo: "bar" })
187
+ })
188
+
189
+ it("should not announce changes to a peer who reconnects", async () => {
190
+ // This test is exercising an issue where a peer who reconnects receives
191
+ // notifications about changes to a document they requested in a
192
+ // previous connection but have not requested since reconnection. This
193
+ // occurs because in order to calculate whether a peer has access to a
194
+ // document the Repo keeps track of whether the given peer has ever
195
+ // requested that document. If this state is not cleared on reconnection
196
+ // then repo will continue to announce changes to the peer in question.
197
+ const { alice, bob } = await twoPeers({
198
+ alice: {
199
+ shareConfig: {
200
+ announce: async () => false,
201
+ access: async () => true,
202
+ },
203
+ },
204
+ bob: { sharePolicy: async () => true },
205
+ })
206
+
207
+ const aliceHandle = alice.create({ foo: "bar" })
208
+ const bobHandle = await bob.find(aliceHandle.url)
209
+ assert(bobHandle != null)
210
+
211
+ // Disconnect everyone
212
+ bob.networkSubsystem.adapters[0].emit("peer-disconnected", {
213
+ peerId: alice.peerId,
214
+ })
215
+ alice.networkSubsystem.adapters[0].emit("peer-disconnected", {
216
+ peerId: bob.peerId,
217
+ })
218
+ bob.networkSubsystem.disconnect()
219
+ alice.networkSubsystem.disconnect()
220
+
221
+ await pause(150)
222
+
223
+ // Create a new repo with the same peer ID and reconnect
224
+ const bob2 = new Repo({ peerId: "bob" as PeerId })
225
+ await connectRepos(alice, bob2)
226
+
227
+ // Now create a third repo and connect it to alice
228
+ const charlie = new Repo({ peerId: "charlie" as PeerId })
229
+ await connectRepos(alice, charlie)
230
+
231
+ // Make a change on charlie, this will send a message to alice
232
+ // who will forward to anyone who she thinks has previously
233
+ // requested the document
234
+ const charlieHandle = await charlie.find<{ foo: string }>(aliceHandle.url)
235
+ charlieHandle.change(d => (d.foo = "baz"))
236
+
237
+ await pause(300)
238
+
239
+ // Bob should not have the handle, i.e. Alice should not have forwarded
240
+ // the messages from charlie
241
+ assert.equal(Object.entries(bob2.handles).length, 0)
242
+ })
243
+ })
244
+ })
@@ -0,0 +1,24 @@
1
+ import { FindProgress } from "../../src/FindProgress.js"
2
+ import { FindProgressWithMethods } from "../../src/Repo.js"
3
+
4
+ export default async function awaitState(
5
+ progress: FindProgress<unknown> | FindProgressWithMethods<unknown>,
6
+ state: string
7
+ ): Promise<void> {
8
+ if (progress.state == state) {
9
+ return
10
+ }
11
+ if (!("subscribe" in progress)) {
12
+ throw new Error(
13
+ `expected progress in state ${state} but was in final state ${progress.state}`
14
+ )
15
+ }
16
+ await new Promise(resolve => {
17
+ const unsubscribe = progress.subscribe(progress => {
18
+ if (progress.state === state) {
19
+ unsubscribe()
20
+ resolve(null)
21
+ }
22
+ })
23
+ })
24
+ }
@@ -0,0 +1,18 @@
1
+ import { DummyNetworkAdapter } from "../../src/helpers/DummyNetworkAdapter.js"
2
+ import { Repo } from "../../src/Repo.js"
3
+ import pause from "./pause.js"
4
+
5
+ export default async function connectRepos(left: Repo, right: Repo) {
6
+ const [leftToRight, rightToLeft] = DummyNetworkAdapter.createConnectedPair({
7
+ latency: 0,
8
+ })
9
+ left.networkSubsystem.addNetworkAdapter(leftToRight)
10
+ right.networkSubsystem.addNetworkAdapter(rightToLeft)
11
+ leftToRight.peerCandidate(right.peerId)
12
+ rightToLeft.peerCandidate(left.peerId)
13
+ await Promise.all([
14
+ left.networkSubsystem.whenReady(),
15
+ right.networkSubsystem.whenReady(),
16
+ ])
17
+ await pause(10)
18
+ }
@@ -0,0 +1,3 @@
1
+ export default async function pause(millis: number) {
2
+ return new Promise(resolve => setTimeout(resolve, millis))
3
+ }
@@ -0,0 +1,30 @@
1
+ import { DummyNetworkAdapter } from "../../src/helpers/DummyNetworkAdapter.js"
2
+ import { Repo, ShareConfig, SharePolicy } from "../../src/Repo.js"
3
+ import { PeerId } from "../../src/types.js"
4
+ import connectRepos from "./connectRepos.js"
5
+
6
+ // The parts of `RepoConfig` which are either the old sharePolicy API or the new shareConfig API
7
+ export type EitherConfig = {
8
+ sharePolicy?: SharePolicy
9
+ shareConfig?: ShareConfig
10
+ }
11
+
12
+ /// Create two connected peers with the given share configurations
13
+ export default async function twoPeers({
14
+ alice: aliceConfig,
15
+ bob: bobConfig,
16
+ }: {
17
+ alice: EitherConfig
18
+ bob: EitherConfig
19
+ }): Promise<{ alice: Repo; bob: Repo }> {
20
+ const alice = new Repo({
21
+ peerId: "alice" as PeerId,
22
+ ...aliceConfig,
23
+ })
24
+ const bob = new Repo({
25
+ peerId: "bob" as PeerId,
26
+ ...bobConfig,
27
+ })
28
+ await connectRepos(alice, bob)
29
+ return { alice, bob }
30
+ }
@@ -0,0 +1,9 @@
1
+ export default async function withTimeout<T>(
2
+ promise: Promise<T>,
3
+ timeout: number
4
+ ): Promise<T | undefined> {
5
+ const timeoutPromise = new Promise<T | undefined>(resolve => {
6
+ setTimeout(() => resolve(undefined), timeout)
7
+ })
8
+ return Promise.race([promise, timeoutPromise])
9
+ }