@automerge/automerge-repo 2.3.1 → 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 +37 -4
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +39 -6
- package/dist/helpers/DummyNetworkAdapter.d.ts.map +1 -1
- package/dist/helpers/DummyNetworkAdapter.js +11 -1
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +19 -2
- package/package.json +2 -2
- package/src/Repo.ts +74 -5
- package/src/helpers/DummyNetworkAdapter.ts +11 -1
- package/src/synchronizer/CollectionSynchronizer.ts +24 -2
- package/test/Repo.test.ts +170 -1
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;
|
package/dist/Repo.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAQ5C,OAAO,EAEL,SAAS,EAKV,MAAM,gBAAgB,CAAA;AAIvB,OAAO,EACL,uBAAuB,EACvB,KAAK,YAAY,EAClB,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAEhE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAA;AACjF,OAAO,EACL,cAAc,EAEf,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,MAAM,EACP,MAAM,YAAY,CAAA;AACnB,OAAO,EAAa,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,MAAM,MAAM,uBAAuB,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG;IACzD,UAAU,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IAChE,IAAI,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,CAAA;IAC3B,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,MAAM,IAAI,CAAA;CACzE,CAAA;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,CAAA;IAC3B,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,MAAM,IAAI,CAAA;IACxE,UAAU,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;CACjE,CAAA;AAMD,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;IAUnC,cAAc;IACd,YAAY,EAAE,sBAAsB,CAAA;
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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) {
|
|
@@ -110,7 +123,8 @@ export class Repo extends EventEmitter {
|
|
|
110
123
|
if (peerMetadata) {
|
|
111
124
|
this.peerMetadataByPeerId[peerId] = { ...peerMetadata };
|
|
112
125
|
}
|
|
113
|
-
this
|
|
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;;
|
|
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;
|
|
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
|
|
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
|
|
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
|
+
"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": "
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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)
|
|
@@ -200,7 +215,8 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
200
215
|
this.peerMetadataByPeerId[peerId] = { ...peerMetadata }
|
|
201
216
|
}
|
|
202
217
|
|
|
203
|
-
this
|
|
218
|
+
this.#shareConfig
|
|
219
|
+
.announce(peerId)
|
|
204
220
|
.then(shouldShare => {
|
|
205
221
|
if (shouldShare && this.#remoteHeadsGossipingEnabled) {
|
|
206
222
|
this.#remoteHeadsSubscriptions.addGenerousPeer(peerId)
|
|
@@ -396,6 +412,30 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
396
412
|
return this.synchronizer.peers
|
|
397
413
|
}
|
|
398
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
|
+
|
|
399
439
|
getStorageIdOfPeer(peerId: PeerId): StorageId | undefined {
|
|
400
440
|
return this.peerMetadataByPeerId[peerId]?.storageId
|
|
401
441
|
}
|
|
@@ -897,9 +937,17 @@ export interface RepoConfig {
|
|
|
897
937
|
/**
|
|
898
938
|
* Normal peers typically share generously with everyone (meaning we sync all our documents with
|
|
899
939
|
* all peers). A server only syncs documents that a peer explicitly requests by ID.
|
|
940
|
+
* @deprecated Use `shareConfig` instead
|
|
900
941
|
*/
|
|
901
942
|
sharePolicy?: SharePolicy
|
|
902
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
|
+
|
|
903
951
|
/**
|
|
904
952
|
* Whether to enable the experimental remote heads gossiping feature
|
|
905
953
|
*/
|
|
@@ -931,6 +979,27 @@ export type SharePolicy = (
|
|
|
931
979
|
documentId?: DocumentId
|
|
932
980
|
) => Promise<boolean>
|
|
933
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
|
+
|
|
934
1003
|
// events & payloads
|
|
935
1004
|
export interface RepoEvents {
|
|
936
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
|
|
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
|
|
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", () => {
|