@automerge/automerge-repo 2.0.3 → 2.0.5
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/DocHandle.d.ts +12 -2
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +18 -7
- package/dist/RemoteHeadsSubscriptions.d.ts.map +1 -1
- package/dist/RemoteHeadsSubscriptions.js +29 -24
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +15 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +6 -1
- package/package.json +2 -2
- package/src/DocHandle.ts +25 -7
- package/src/RemoteHeadsSubscriptions.ts +31 -30
- package/src/Repo.ts +21 -9
- package/src/index.ts +1 -0
- package/src/synchronizer/DocSynchronizer.ts +6 -1
- package/test/RemoteHeadsSubscriptions.test.ts +8 -7
- package/test/Repo.test.ts +66 -6
- package/test/remoteHeads.test.ts +13 -3
package/dist/DocHandle.d.ts
CHANGED
|
@@ -153,9 +153,14 @@ export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
|
|
|
153
153
|
* Called by the repo when a doc handle changes or we receive new remote heads.
|
|
154
154
|
* @hidden
|
|
155
155
|
*/
|
|
156
|
-
|
|
157
|
-
/** Returns the heads of the storageId.
|
|
156
|
+
setSyncInfo(storageId: StorageId, syncInfo: SyncInfo): void;
|
|
157
|
+
/** Returns the heads of the storageId.
|
|
158
|
+
*
|
|
159
|
+
* @deprecated Use getSyncInfo instead.
|
|
160
|
+
*/
|
|
158
161
|
getRemoteHeads(storageId: StorageId): UrlHeads | undefined;
|
|
162
|
+
/** Returns the heads and the timestamp of the last update for the storageId. */
|
|
163
|
+
getSyncInfo(storageId: StorageId): SyncInfo | undefined;
|
|
159
164
|
/**
|
|
160
165
|
* All changes to an Automerge document should be made through this method.
|
|
161
166
|
* Inside the callback, the document should be treated as mutable: all edits will be recorded
|
|
@@ -218,6 +223,10 @@ export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
|
|
|
218
223
|
numChanges: number;
|
|
219
224
|
};
|
|
220
225
|
}
|
|
226
|
+
export type SyncInfo = {
|
|
227
|
+
lastHeads: UrlHeads;
|
|
228
|
+
lastSyncTimestamp: number;
|
|
229
|
+
};
|
|
221
230
|
/** @hidden */
|
|
222
231
|
export type DocHandleOptions<T> = {
|
|
223
232
|
/** If we know this is a new document (because we're creating it) this should be set to true. */
|
|
@@ -278,6 +287,7 @@ export interface DocHandleOutboundEphemeralMessagePayload<T> {
|
|
|
278
287
|
export interface DocHandleRemoteHeadsPayload {
|
|
279
288
|
storageId: StorageId;
|
|
280
289
|
heads: UrlHeads;
|
|
290
|
+
timestamp: number;
|
|
281
291
|
}
|
|
282
292
|
/**
|
|
283
293
|
* Possible internal states for a DocHandle
|
package/dist/DocHandle.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocHandle.d.ts","sourceRoot":"","sources":["../src/DocHandle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,2BAA2B,CAAA;AAErD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAU5C,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC5E,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAE9C;;;;;;;;;;;;GAYG;AACH,qBAAa,SAAS,CAAC,CAAC,CAAE,SAAQ,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;;IAwBvD,UAAU,EAAE,UAAU;IAF/B,cAAc;gBAEL,UAAU,EAAE,UAAU,EAC7B,OAAO,GAAE,gBAAgB,CAAC,CAAC,CAAM;IAqKnC;OACG;IACH,IAAI,GAAG,IAAI,YAAY,CAKtB;IAED;;;;;OAKG;IACH,OAAO,gBAAgC;IAEvC;;;;;OAKG;IACH,UAAU,gBAAmC;IAE7C;;;;;OAKG;IACH,SAAS,gBAAkC;IAE3C;;;;OAIG;IACH,aAAa,gBAAsC;IAEnD;;OAEG;IACH,OAAO,GAAI,QAAQ,WAAW,EAAE,aAC0B;IAE1D,cAAc;IACd,IAAI,KAAK,yFAER;IAED;;;;;;OAMG;IACG,SAAS,CAAC,WAAW,GAAE,WAAW,EAAc;IAItD;;;;;;OAMG;IACH,GAAG;IAQH;;qBAEiB;IACjB,OAAO;IAOP;;;;OAIG;IACH,KAAK,IAAI,QAAQ;IAQjB,KAAK;IAIL;;;;;;;;;;;OAWG;IACH,OAAO,IAAI,QAAQ,EAAE,GAAG,SAAS;IAWjC;;;;;;;;;;;;OAYG;IACH,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC;IA8BnC;;;;;;;;;;;;OAYG;IACH,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,EAAE;IAkClE;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,aAAa,GAAG,SAAS;IAetD;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAI5C;;;;OAIG;IACH,WAAW;IAIX;;;OAGG;IACH,
|
|
1
|
+
{"version":3,"file":"DocHandle.d.ts","sourceRoot":"","sources":["../src/DocHandle.ts"],"names":[],"mappings":"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"}
|
package/dist/DocHandle.js
CHANGED
|
@@ -31,8 +31,8 @@ export class DocHandle extends EventEmitter {
|
|
|
31
31
|
/** How long to wait before giving up on a document. (Note that a document will be marked
|
|
32
32
|
* unavailable much sooner if all known peers respond that they don't have it.) */
|
|
33
33
|
#timeoutDelay = 60_000;
|
|
34
|
-
/** A dictionary mapping each peer to the last heads we
|
|
35
|
-
#
|
|
34
|
+
/** A dictionary mapping each peer to the last known heads we have. */
|
|
35
|
+
#syncInfoByStorageId = {};
|
|
36
36
|
/** Cache for view handles, keyed by the stringified heads */
|
|
37
37
|
#viewCache = new Map();
|
|
38
38
|
/** @hidden */
|
|
@@ -403,13 +403,24 @@ export class DocHandle extends EventEmitter {
|
|
|
403
403
|
* Called by the repo when a doc handle changes or we receive new remote heads.
|
|
404
404
|
* @hidden
|
|
405
405
|
*/
|
|
406
|
-
|
|
407
|
-
this.#
|
|
408
|
-
this.emit("remote-heads", {
|
|
406
|
+
setSyncInfo(storageId, syncInfo) {
|
|
407
|
+
this.#syncInfoByStorageId[storageId] = syncInfo;
|
|
408
|
+
this.emit("remote-heads", {
|
|
409
|
+
storageId,
|
|
410
|
+
heads: syncInfo.lastHeads,
|
|
411
|
+
timestamp: syncInfo.lastSyncTimestamp,
|
|
412
|
+
});
|
|
409
413
|
}
|
|
410
|
-
/** Returns the heads of the storageId.
|
|
414
|
+
/** Returns the heads of the storageId.
|
|
415
|
+
*
|
|
416
|
+
* @deprecated Use getSyncInfo instead.
|
|
417
|
+
*/
|
|
411
418
|
getRemoteHeads(storageId) {
|
|
412
|
-
return this.#
|
|
419
|
+
return this.#syncInfoByStorageId[storageId]?.lastHeads;
|
|
420
|
+
}
|
|
421
|
+
/** Returns the heads and the timestamp of the last update for the storageId. */
|
|
422
|
+
getSyncInfo(storageId) {
|
|
423
|
+
return this.#syncInfoByStorageId[storageId];
|
|
413
424
|
}
|
|
414
425
|
/**
|
|
415
426
|
* All changes to an Automerge document should be made through this method.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RemoteHeadsSubscriptions.d.ts","sourceRoot":"","sources":["../src/RemoteHeadsSubscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACzD,OAAO,EACL,kBAAkB,EAClB,gCAAgC,EACjC,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"RemoteHeadsSubscriptions.d.ts","sourceRoot":"","sources":["../src/RemoteHeadsSubscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACzD,OAAO,EACL,kBAAkB,EAClB,gCAAgC,EACjC,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAKtC,MAAM,MAAM,mCAAmC,GAAG;IAChD,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;IACpB,WAAW,EAAE,QAAQ,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAGD,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;IACpB,KAAK,EAAE,QAAQ,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,KAAK,6BAA6B,GAAG;IACnC,sBAAsB,EAAE,CAAC,OAAO,EAAE,mCAAmC,KAAK,IAAI,CAAA;IAC9E,oBAAoB,EAAE,CAAC,OAAO,EAAE;QAC9B,KAAK,EAAE,MAAM,EAAE,CAAA;QACf,GAAG,CAAC,EAAE,SAAS,EAAE,CAAA;QACjB,MAAM,CAAC,EAAE,SAAS,EAAE,CAAA;KACrB,KAAK,IAAI,CAAA;IACV,qBAAqB,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,IAAI,CAAA;CACnE,CAAA;AAED,qBAAa,wBAAyB,SAAQ,YAAY,CAAC,6BAA6B,CAAC;;IAcvF,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE;IAkBvC,sBAAsB,CAAC,OAAO,EAAE,SAAS,EAAE;IAsB3C,oBAAoB,CAAC,OAAO,EAAE,gCAAgC;IA0E9D,sEAAsE;IACtE,iBAAiB,CAAC,GAAG,EAAE,kBAAkB;IAgDzC,kEAAkE;IAClE,iCAAiC,CAC/B,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,QAAQ;IAqCjB,eAAe,CAAC,MAAM,EAAE,MAAM;IAwB9B,UAAU,CAAC,MAAM,EAAE,MAAM;IA2BzB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU;CAuE1D"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { EventEmitter } from "eventemitter3";
|
|
2
2
|
import debug from "debug";
|
|
3
3
|
export class RemoteHeadsSubscriptions extends EventEmitter {
|
|
4
|
-
//
|
|
5
|
-
#
|
|
4
|
+
// Last known heads and timestamp for each storageId that we know about
|
|
5
|
+
#syncInfoByDocId = new Map();
|
|
6
6
|
// Storage IDs we have subscribed to via Repo.subscribeToRemoteHeads
|
|
7
7
|
#ourSubscriptions = new Set();
|
|
8
8
|
// Storage IDs other peers have subscribed to by sending us a control message
|
|
@@ -91,17 +91,17 @@ export class RemoteHeadsSubscriptions extends EventEmitter {
|
|
|
91
91
|
const subscribedDocs = this.#subscribedDocsByPeer.get(control.senderId);
|
|
92
92
|
if (subscribedDocs) {
|
|
93
93
|
for (const documentId of subscribedDocs) {
|
|
94
|
-
const
|
|
95
|
-
if (!
|
|
94
|
+
const syncInfo = this.#syncInfoByDocId.get(documentId);
|
|
95
|
+
if (!syncInfo) {
|
|
96
96
|
continue;
|
|
97
97
|
}
|
|
98
|
-
const
|
|
99
|
-
if (
|
|
98
|
+
const syncInfoForRemote = syncInfo.get(remote);
|
|
99
|
+
if (syncInfoForRemote) {
|
|
100
100
|
this.emit("notify-remote-heads", {
|
|
101
101
|
targetId: control.senderId,
|
|
102
102
|
documentId,
|
|
103
|
-
heads: lastHeads
|
|
104
|
-
timestamp:
|
|
103
|
+
heads: syncInfoForRemote.lastHeads,
|
|
104
|
+
timestamp: syncInfoForRemote.lastSyncTimestamp,
|
|
105
105
|
storageId: remote,
|
|
106
106
|
});
|
|
107
107
|
}
|
|
@@ -156,15 +156,20 @@ export class RemoteHeadsSubscriptions extends EventEmitter {
|
|
|
156
156
|
/** A peer we are directly connected to has updated their heads */
|
|
157
157
|
handleImmediateRemoteHeadsChanged(documentId, storageId, heads) {
|
|
158
158
|
this.#log("handleLocalHeadsChanged", documentId, storageId, heads);
|
|
159
|
-
const remote = this.#
|
|
159
|
+
const remote = this.#syncInfoByDocId.get(documentId);
|
|
160
160
|
const timestamp = Date.now();
|
|
161
161
|
if (!remote) {
|
|
162
|
-
this.#
|
|
162
|
+
this.#syncInfoByDocId.set(documentId, new Map([
|
|
163
|
+
[storageId, { lastSyncTimestamp: timestamp, lastHeads: heads }],
|
|
164
|
+
]));
|
|
163
165
|
}
|
|
164
166
|
else {
|
|
165
167
|
const docRemote = remote.get(storageId);
|
|
166
|
-
if (!docRemote || docRemote.
|
|
167
|
-
remote.set(storageId, {
|
|
168
|
+
if (!docRemote || docRemote.lastSyncTimestamp < Date.now()) {
|
|
169
|
+
remote.set(storageId, {
|
|
170
|
+
lastSyncTimestamp: Date.now(),
|
|
171
|
+
lastHeads: heads,
|
|
172
|
+
});
|
|
168
173
|
}
|
|
169
174
|
}
|
|
170
175
|
const theirSubs = this.#theirSubscriptions.get(storageId);
|
|
@@ -191,13 +196,13 @@ export class RemoteHeadsSubscriptions extends EventEmitter {
|
|
|
191
196
|
peers: [peerId],
|
|
192
197
|
});
|
|
193
198
|
}
|
|
194
|
-
for (const [documentId, remote] of this.#
|
|
195
|
-
for (const [storageId, {
|
|
199
|
+
for (const [documentId, remote] of this.#syncInfoByDocId) {
|
|
200
|
+
for (const [storageId, { lastHeads, lastSyncTimestamp }] of remote) {
|
|
196
201
|
this.emit("notify-remote-heads", {
|
|
197
202
|
targetId: peerId,
|
|
198
203
|
documentId: documentId,
|
|
199
|
-
heads:
|
|
200
|
-
timestamp:
|
|
204
|
+
heads: lastHeads,
|
|
205
|
+
timestamp: lastSyncTimestamp,
|
|
201
206
|
storageId: storageId,
|
|
202
207
|
});
|
|
203
208
|
}
|
|
@@ -231,7 +236,7 @@ export class RemoteHeadsSubscriptions extends EventEmitter {
|
|
|
231
236
|
this.#subscribedDocsByPeer.set(peerId, subscribedDocs);
|
|
232
237
|
}
|
|
233
238
|
subscribedDocs.add(documentId);
|
|
234
|
-
const remoteHeads = this.#
|
|
239
|
+
const remoteHeads = this.#syncInfoByDocId.get(documentId);
|
|
235
240
|
if (remoteHeads) {
|
|
236
241
|
for (const [storageId, lastHeads] of remoteHeads) {
|
|
237
242
|
const subscribedPeers = this.#theirSubscriptions.get(storageId);
|
|
@@ -239,8 +244,8 @@ export class RemoteHeadsSubscriptions extends EventEmitter {
|
|
|
239
244
|
this.emit("notify-remote-heads", {
|
|
240
245
|
targetId: peerId,
|
|
241
246
|
documentId,
|
|
242
|
-
heads: lastHeads.
|
|
243
|
-
timestamp: lastHeads.
|
|
247
|
+
heads: lastHeads.lastHeads,
|
|
248
|
+
timestamp: lastHeads.lastSyncTimestamp,
|
|
244
249
|
storageId,
|
|
245
250
|
});
|
|
246
251
|
}
|
|
@@ -260,19 +265,19 @@ export class RemoteHeadsSubscriptions extends EventEmitter {
|
|
|
260
265
|
!this.#theirSubscriptions.has(storageId)) {
|
|
261
266
|
continue;
|
|
262
267
|
}
|
|
263
|
-
let remote = this.#
|
|
268
|
+
let remote = this.#syncInfoByDocId.get(documentId);
|
|
264
269
|
if (!remote) {
|
|
265
270
|
remote = new Map();
|
|
266
|
-
this.#
|
|
271
|
+
this.#syncInfoByDocId.set(documentId, remote);
|
|
267
272
|
}
|
|
268
273
|
const docRemote = remote.get(storageId);
|
|
269
|
-
if (docRemote && docRemote.
|
|
274
|
+
if (docRemote && docRemote.lastSyncTimestamp >= timestamp) {
|
|
270
275
|
continue;
|
|
271
276
|
}
|
|
272
277
|
else {
|
|
273
278
|
remote.set(storageId, {
|
|
274
|
-
timestamp,
|
|
275
|
-
|
|
279
|
+
lastSyncTimestamp: timestamp,
|
|
280
|
+
lastHeads: heads,
|
|
276
281
|
});
|
|
277
282
|
changedHeads.push({
|
|
278
283
|
documentId,
|
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;IAEnC,mDAAmD;IACnD,cAAc;IACd,gBAAgB,SAAM;IAItB,cAAc;IACd,YAAY,EAAE,sBAAsB,CAAA;IAEpC,sDAAsD;IACtD,cAAc;IACd,WAAW,EAAE,WAAW,CAAmB;IAE3C,8GAA8G;IAC9G,cAAc;IACd,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAK;gBAM3C,EACV,OAAO,EACP,OAAY,EACZ,MAAuB,EACvB,WAAW,EACX,WAAmC,EACnC,0BAAkC,EAClC,QAAa,GACd,GAAE,UAAe;
|
|
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;IAEnC,mDAAmD;IACnD,cAAc;IACd,gBAAgB,SAAM;IAItB,cAAc;IACd,YAAY,EAAE,sBAAsB,CAAA;IAEpC,sDAAsD;IACtD,cAAc;IACd,WAAW,EAAE,WAAW,CAAmB;IAE3C,8GAA8G;IAC9G,cAAc;IACd,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAK;gBAM3C,EACV,OAAO,EACP,OAAY,EACZ,MAAuB,EACvB,WAAW,EACX,WAAmC,EACnC,0BAAkC,EAClC,QAAa,GACd,GAAE,UAAe;IA+PlB,8CAA8C;IAC9C,IAAI,OAAO,uCAEV;IAED,+CAA+C;IAC/C,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;IAED,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAIzD;;;;OAIG;IACH,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;IAuBzC;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC;IAmBnC,gBAAgB,CAAC,CAAC,EAChB,EAAE,EAAE,aAAa,EACjB,OAAO,GAAE,YAAiB,GACzB,uBAAuB,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;IAgKzC,IAAI,CAAC,CAAC,EACV,EAAE,EAAE,aAAa,EACjB,OAAO,GAAE,eAAe,GAAG,YAAiB,GAC3C,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IA0ExB;;;OAGG;IACG,WAAW,CAAC,CAAC;IACjB,sDAAsD;IACtD,EAAE,EAAE,aAAa,EACjB,OAAO,GAAE,eAAe,GAAG,YAAiB,GAC3C,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAmBxB,MAAM;IACJ,oDAAoD;IACpD,EAAE,EAAE,aAAa;IAYnB;;;;;;OAMG;IACG,MAAM,CAAC,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAQhE;;;OAGG;IACH,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU;IAY5B,kBAAkB,GAAI,SAAS,SAAS,EAAE,UASzC;IAED,SAAS,QAAa,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAMnD;IAED;;;;;OAKG;IACG,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAcpD;;;;;OAKG;IACG,eAAe,CAAC,UAAU,EAAE,UAAU;IA6B5C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAOzB,OAAO,IAAI;QAAE,SAAS,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;SAAE,CAAA;KAAE;CAGjD;AAED,MAAM,WAAW,UAAU;IACzB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;8DAC0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAA;IAErB,gDAAgD;IAChD,OAAO,CAAC,EAAE,uBAAuB,CAAA;IAEjC,iEAAiE;IACjE,OAAO,CAAC,EAAE,uBAAuB,EAAE,CAAA;IAEnC;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;IAEzB;;OAEG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAA;IAEpC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAA;CAC1B;AAED;;;;;;;KAOK;AACL,MAAM,MAAM,WAAW,GAAG,CACxB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,UAAU,KACpB,OAAO,CAAC,OAAO,CAAC,CAAA;AAGrB,MAAM,WAAW,UAAU;IACzB,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAA;IACxC,6BAA6B;IAC7B,iBAAiB,EAAE,CAAC,GAAG,EAAE,qBAAqB,KAAK,IAAI,CAAA;IACvD,4FAA4F;IAC5F,sBAAsB,EAAE,CAAC,GAAG,EAAE,qBAAqB,KAAK,IAAI,CAAA;IAC5D,aAAa,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI,CAAA;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,CAAA;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,UAAU,CAAA;CACvB;AAED,MAAM,MAAM,UAAU,GAClB,cAAc,GACd;IACE,IAAI,EAAE,YAAY,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;CACnB,GACD;IACE,IAAI,EAAE,YAAY,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA"}
|
package/dist/Repo.js
CHANGED
|
@@ -118,12 +118,15 @@ export class Repo extends EventEmitter {
|
|
|
118
118
|
if (!storageId) {
|
|
119
119
|
return;
|
|
120
120
|
}
|
|
121
|
-
const heads = handle.
|
|
121
|
+
const heads = handle.getSyncInfo(storageId)?.lastHeads;
|
|
122
122
|
const haveHeadsChanged = message.syncState.theirHeads &&
|
|
123
123
|
(!heads ||
|
|
124
124
|
!headsAreSame(heads, encodeHeads(message.syncState.theirHeads)));
|
|
125
125
|
if (haveHeadsChanged && message.syncState.theirHeads) {
|
|
126
|
-
handle.
|
|
126
|
+
handle.setSyncInfo(storageId, {
|
|
127
|
+
lastHeads: encodeHeads(message.syncState.theirHeads),
|
|
128
|
+
lastSyncTimestamp: Date.now(),
|
|
129
|
+
});
|
|
127
130
|
if (storageId && this.#remoteHeadsGossipingEnabled) {
|
|
128
131
|
this.#remoteHeadsSubscriptions.handleImmediateRemoteHeadsChanged(message.documentId, storageId, encodeHeads(message.syncState.theirHeads));
|
|
129
132
|
}
|
|
@@ -154,9 +157,12 @@ export class Repo extends EventEmitter {
|
|
|
154
157
|
});
|
|
155
158
|
}
|
|
156
159
|
});
|
|
157
|
-
this.#remoteHeadsSubscriptions.on("remote-heads-changed",
|
|
158
|
-
const handle = this.#handleCache[
|
|
159
|
-
handle.
|
|
160
|
+
this.#remoteHeadsSubscriptions.on("remote-heads-changed", ({ documentId, storageId, remoteHeads, timestamp }) => {
|
|
161
|
+
const handle = this.#handleCache[documentId];
|
|
162
|
+
handle.setSyncInfo(storageId, {
|
|
163
|
+
lastHeads: remoteHeads,
|
|
164
|
+
lastSyncTimestamp: timestamp,
|
|
165
|
+
});
|
|
160
166
|
});
|
|
161
167
|
}
|
|
162
168
|
}
|
|
@@ -449,6 +455,10 @@ export class Repo extends EventEmitter {
|
|
|
449
455
|
}
|
|
450
456
|
// If the handle isn't ready, wait for it and then return it
|
|
451
457
|
await progress.handle.whenReady([READY, UNAVAILABLE]);
|
|
458
|
+
if (progress.handle.state === "unavailable" &&
|
|
459
|
+
!allowableStates.includes(UNAVAILABLE)) {
|
|
460
|
+
throw new Error(`Document ${id} is unavailable`);
|
|
461
|
+
}
|
|
452
462
|
return progress.handle;
|
|
453
463
|
}
|
|
454
464
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export type { StorageAdapterInterface } from "./storage/StorageAdapterInterface.
|
|
|
36
36
|
import { next as Automerge } from "@automerge/automerge/slim";
|
|
37
37
|
/** @hidden **/
|
|
38
38
|
export * as cbor from "./helpers/cbor.js";
|
|
39
|
-
export type { DocHandleChangePayload, DocHandleDeletePayload, DocHandleEncodedChangePayload, DocHandleEphemeralMessagePayload, DocHandleRemoteHeadsPayload, DocHandleEvents, DocHandleOptions, DocHandleOutboundEphemeralMessagePayload, HandleState, } from "./DocHandle.js";
|
|
39
|
+
export type { DocHandleChangePayload, DocHandleDeletePayload, DocHandleEncodedChangePayload, DocHandleEphemeralMessagePayload, DocHandleRemoteHeadsPayload, DocHandleEvents, DocHandleOptions, DocHandleOutboundEphemeralMessagePayload, HandleState, SyncInfo, } from "./DocHandle.js";
|
|
40
40
|
export type { DeleteDocumentPayload, DocumentPayload, RepoConfig, RepoEvents, SharePolicy, } from "./Repo.js";
|
|
41
41
|
export type { NetworkAdapterEvents, OpenPayload, PeerCandidatePayload, PeerDisconnectedPayload, PeerMetadata, } from "./network/NetworkAdapterInterface.js";
|
|
42
42
|
export type { NetworkSubsystemEvents, PeerPayload, } from "./network/NetworkSubsystem.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,WAAW,EACX,WAAW,GACZ,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AACnF,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAE7D,eAAe;AACf,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAA;AAIzC,YAAY,EACV,sBAAsB,EACtB,sBAAsB,EACtB,6BAA6B,EAC7B,gCAAgC,EAChC,2BAA2B,EAC3B,eAAe,EACf,gBAAgB,EAChB,wCAAwC,EACxC,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/DocSynchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,2BAA2B,CAAA;AAGrD,OAAO,EACL,SAAS,EAKV,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAEL,gBAAgB,EAEhB,WAAW,EACX,cAAc,EACd,WAAW,EAEZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhD,KAAK,kBAAkB,GAAG,SAAS,GAAG,KAAK,GAAG,aAAa,GAAG,OAAO,CAAA;AAOrE,UAAU,qBAAqB;IAC7B,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC,CAAA;CACvE;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;;IAE/C,gBAAgB,SAAM;gBAyBV,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,qBAAqB;IAyBtE,IAAI,UAAU,uCAEb;IAED,IAAI,UAAU,qCAEb;IAqID,OAAO,CAAC,MAAM,EAAE,MAAM;IAIhB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IA8DjC,OAAO,CAAC,MAAM,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"DocSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/DocSynchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,2BAA2B,CAAA;AAGrD,OAAO,EACL,SAAS,EAKV,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAEL,gBAAgB,EAEhB,WAAW,EACX,cAAc,EACd,WAAW,EAEZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhD,KAAK,kBAAkB,GAAG,SAAS,GAAG,KAAK,GAAG,aAAa,GAAG,OAAO,CAAA;AAOrE,UAAU,qBAAqB;IAC7B,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC,CAAA;CACvE;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;;IAE/C,gBAAgB,SAAM;gBAyBV,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,qBAAqB;IAyBtE,IAAI,UAAU,uCAEb;IAED,IAAI,UAAU,qCAEb;IAqID,OAAO,CAAC,MAAM,EAAE,MAAM;IAIhB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IA8DjC,OAAO,CAAC,MAAM,EAAE,MAAM;IAOtB,cAAc,CAAC,OAAO,EAAE,WAAW;IAkBnC,uBAAuB,CAAC,OAAO,EAAE,gBAAgB;IAuBjD,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc;IA2FxD,OAAO,IAAI;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;CAM7E"}
|
|
@@ -209,6 +209,8 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
209
209
|
endSync(peerId) {
|
|
210
210
|
this.#log(`removing peer ${peerId}`);
|
|
211
211
|
this.#peers = this.#peers.filter(p => p !== peerId);
|
|
212
|
+
delete this.#peerDocumentStatuses[peerId];
|
|
213
|
+
this.#checkDocUnavailable();
|
|
212
214
|
}
|
|
213
215
|
receiveMessage(message) {
|
|
214
216
|
switch (message.type) {
|
|
@@ -288,12 +290,15 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
288
290
|
#checkDocUnavailable() {
|
|
289
291
|
// if we know none of the peers have the document, tell all our peers that we don't either
|
|
290
292
|
if (this.#syncStarted &&
|
|
291
|
-
this.#handle.inState([REQUESTING]) &&
|
|
293
|
+
this.#handle.inState([REQUESTING, UNAVAILABLE]) &&
|
|
292
294
|
this.#peers.every(peerId => this.#peerDocumentStatuses[peerId] === "unavailable" ||
|
|
293
295
|
this.#peerDocumentStatuses[peerId] === "wants")) {
|
|
294
296
|
this.#peers
|
|
295
297
|
.filter(peerId => this.#peerDocumentStatuses[peerId] === "wants")
|
|
296
298
|
.forEach(peerId => {
|
|
299
|
+
// Transition the peer to unavailable so that we don't send it a doc-unavailable
|
|
300
|
+
// message every time we run #checkDocUnavailable
|
|
301
|
+
this.#peerDocumentStatuses[peerId] = "unavailable";
|
|
297
302
|
const message = {
|
|
298
303
|
type: "doc-unavailable",
|
|
299
304
|
documentId: this.#handle.documentId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
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": "9d24ea3db7c1f459fc66ce43b794a9ed4bef1341"
|
|
63
63
|
}
|
package/src/DocHandle.ts
CHANGED
|
@@ -42,8 +42,8 @@ export class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
|
|
|
42
42
|
* unavailable much sooner if all known peers respond that they don't have it.) */
|
|
43
43
|
#timeoutDelay = 60_000
|
|
44
44
|
|
|
45
|
-
/** A dictionary mapping each peer to the last heads we
|
|
46
|
-
#
|
|
45
|
+
/** A dictionary mapping each peer to the last known heads we have. */
|
|
46
|
+
#syncInfoByStorageId: Record<StorageId, SyncInfo> = {}
|
|
47
47
|
|
|
48
48
|
/** Cache for view handles, keyed by the stringified heads */
|
|
49
49
|
#viewCache: Map<string, DocHandle<T>> = new Map()
|
|
@@ -482,14 +482,26 @@ export class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
|
|
|
482
482
|
* Called by the repo when a doc handle changes or we receive new remote heads.
|
|
483
483
|
* @hidden
|
|
484
484
|
*/
|
|
485
|
-
|
|
486
|
-
this.#
|
|
487
|
-
this.emit("remote-heads", {
|
|
485
|
+
setSyncInfo(storageId: StorageId, syncInfo: SyncInfo) {
|
|
486
|
+
this.#syncInfoByStorageId[storageId] = syncInfo
|
|
487
|
+
this.emit("remote-heads", {
|
|
488
|
+
storageId,
|
|
489
|
+
heads: syncInfo.lastHeads,
|
|
490
|
+
timestamp: syncInfo.lastSyncTimestamp,
|
|
491
|
+
})
|
|
488
492
|
}
|
|
489
493
|
|
|
490
|
-
/** Returns the heads of the storageId.
|
|
494
|
+
/** Returns the heads of the storageId.
|
|
495
|
+
*
|
|
496
|
+
* @deprecated Use getSyncInfo instead.
|
|
497
|
+
*/
|
|
491
498
|
getRemoteHeads(storageId: StorageId): UrlHeads | undefined {
|
|
492
|
-
return this.#
|
|
499
|
+
return this.#syncInfoByStorageId[storageId]?.lastHeads
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/** Returns the heads and the timestamp of the last update for the storageId. */
|
|
503
|
+
getSyncInfo(storageId: StorageId): SyncInfo | undefined {
|
|
504
|
+
return this.#syncInfoByStorageId[storageId]
|
|
493
505
|
}
|
|
494
506
|
|
|
495
507
|
/**
|
|
@@ -642,6 +654,11 @@ export class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
|
|
|
642
654
|
|
|
643
655
|
// TYPES
|
|
644
656
|
|
|
657
|
+
export type SyncInfo = {
|
|
658
|
+
lastHeads: UrlHeads
|
|
659
|
+
lastSyncTimestamp: number
|
|
660
|
+
}
|
|
661
|
+
|
|
645
662
|
/** @hidden */
|
|
646
663
|
export type DocHandleOptions<T> =
|
|
647
664
|
// NEW DOCUMENTS
|
|
@@ -722,6 +739,7 @@ export interface DocHandleOutboundEphemeralMessagePayload<T> {
|
|
|
722
739
|
export interface DocHandleRemoteHeadsPayload {
|
|
723
740
|
storageId: StorageId
|
|
724
741
|
heads: UrlHeads
|
|
742
|
+
timestamp: number
|
|
725
743
|
}
|
|
726
744
|
|
|
727
745
|
// STATE MACHINE TYPES & CONSTANTS
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "./network/messages.js"
|
|
7
7
|
import { StorageId } from "./index.js"
|
|
8
8
|
import debug from "debug"
|
|
9
|
+
import { SyncInfo } from "./DocHandle.js"
|
|
9
10
|
|
|
10
11
|
// Notify a DocHandle that remote heads have changed
|
|
11
12
|
export type RemoteHeadsSubscriptionEventPayload = {
|
|
@@ -35,8 +36,8 @@ type RemoteHeadsSubscriptionEvents = {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
export class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSubscriptionEvents> {
|
|
38
|
-
//
|
|
39
|
-
#
|
|
39
|
+
// Last known heads and timestamp for each storageId that we know about
|
|
40
|
+
#syncInfoByDocId: Map<DocumentId, Map<StorageId, SyncInfo>> = new Map()
|
|
40
41
|
// Storage IDs we have subscribed to via Repo.subscribeToRemoteHeads
|
|
41
42
|
#ourSubscriptions: Set<StorageId> = new Set()
|
|
42
43
|
// Storage IDs other peers have subscribed to by sending us a control message
|
|
@@ -142,18 +143,18 @@ export class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSubscripti
|
|
|
142
143
|
const subscribedDocs = this.#subscribedDocsByPeer.get(control.senderId)
|
|
143
144
|
if (subscribedDocs) {
|
|
144
145
|
for (const documentId of subscribedDocs) {
|
|
145
|
-
const
|
|
146
|
-
if (!
|
|
146
|
+
const syncInfo = this.#syncInfoByDocId.get(documentId)
|
|
147
|
+
if (!syncInfo) {
|
|
147
148
|
continue
|
|
148
149
|
}
|
|
149
150
|
|
|
150
|
-
const
|
|
151
|
-
if (
|
|
151
|
+
const syncInfoForRemote = syncInfo.get(remote)
|
|
152
|
+
if (syncInfoForRemote) {
|
|
152
153
|
this.emit("notify-remote-heads", {
|
|
153
154
|
targetId: control.senderId,
|
|
154
155
|
documentId,
|
|
155
|
-
heads: lastHeads
|
|
156
|
-
timestamp:
|
|
156
|
+
heads: syncInfoForRemote.lastHeads,
|
|
157
|
+
timestamp: syncInfoForRemote.lastSyncTimestamp,
|
|
157
158
|
storageId: remote,
|
|
158
159
|
})
|
|
159
160
|
}
|
|
@@ -218,17 +219,22 @@ export class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSubscripti
|
|
|
218
219
|
heads: UrlHeads
|
|
219
220
|
) {
|
|
220
221
|
this.#log("handleLocalHeadsChanged", documentId, storageId, heads)
|
|
221
|
-
const remote = this.#
|
|
222
|
+
const remote = this.#syncInfoByDocId.get(documentId)
|
|
222
223
|
const timestamp = Date.now()
|
|
223
224
|
if (!remote) {
|
|
224
|
-
this.#
|
|
225
|
+
this.#syncInfoByDocId.set(
|
|
225
226
|
documentId,
|
|
226
|
-
new Map([
|
|
227
|
+
new Map([
|
|
228
|
+
[storageId, { lastSyncTimestamp: timestamp, lastHeads: heads }],
|
|
229
|
+
])
|
|
227
230
|
)
|
|
228
231
|
} else {
|
|
229
232
|
const docRemote = remote.get(storageId)
|
|
230
|
-
if (!docRemote || docRemote.
|
|
231
|
-
remote.set(storageId, {
|
|
233
|
+
if (!docRemote || docRemote.lastSyncTimestamp < Date.now()) {
|
|
234
|
+
remote.set(storageId, {
|
|
235
|
+
lastSyncTimestamp: Date.now(),
|
|
236
|
+
lastHeads: heads,
|
|
237
|
+
})
|
|
232
238
|
}
|
|
233
239
|
}
|
|
234
240
|
const theirSubs = this.#theirSubscriptions.get(storageId)
|
|
@@ -258,13 +264,13 @@ export class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSubscripti
|
|
|
258
264
|
})
|
|
259
265
|
}
|
|
260
266
|
|
|
261
|
-
for (const [documentId, remote] of this.#
|
|
262
|
-
for (const [storageId, {
|
|
267
|
+
for (const [documentId, remote] of this.#syncInfoByDocId) {
|
|
268
|
+
for (const [storageId, { lastHeads, lastSyncTimestamp }] of remote) {
|
|
263
269
|
this.emit("notify-remote-heads", {
|
|
264
270
|
targetId: peerId,
|
|
265
271
|
documentId: documentId,
|
|
266
|
-
heads:
|
|
267
|
-
timestamp:
|
|
272
|
+
heads: lastHeads,
|
|
273
|
+
timestamp: lastSyncTimestamp,
|
|
268
274
|
storageId: storageId,
|
|
269
275
|
})
|
|
270
276
|
}
|
|
@@ -307,7 +313,7 @@ export class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSubscripti
|
|
|
307
313
|
|
|
308
314
|
subscribedDocs.add(documentId)
|
|
309
315
|
|
|
310
|
-
const remoteHeads = this.#
|
|
316
|
+
const remoteHeads = this.#syncInfoByDocId.get(documentId)
|
|
311
317
|
if (remoteHeads) {
|
|
312
318
|
for (const [storageId, lastHeads] of remoteHeads) {
|
|
313
319
|
const subscribedPeers = this.#theirSubscriptions.get(storageId)
|
|
@@ -315,8 +321,8 @@ export class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSubscripti
|
|
|
315
321
|
this.emit("notify-remote-heads", {
|
|
316
322
|
targetId: peerId,
|
|
317
323
|
documentId,
|
|
318
|
-
heads: lastHeads.
|
|
319
|
-
timestamp: lastHeads.
|
|
324
|
+
heads: lastHeads.lastHeads,
|
|
325
|
+
timestamp: lastHeads.lastSyncTimestamp,
|
|
320
326
|
storageId,
|
|
321
327
|
})
|
|
322
328
|
}
|
|
@@ -345,19 +351,19 @@ export class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSubscripti
|
|
|
345
351
|
) {
|
|
346
352
|
continue
|
|
347
353
|
}
|
|
348
|
-
let remote = this.#
|
|
354
|
+
let remote = this.#syncInfoByDocId.get(documentId)
|
|
349
355
|
if (!remote) {
|
|
350
356
|
remote = new Map()
|
|
351
|
-
this.#
|
|
357
|
+
this.#syncInfoByDocId.set(documentId, remote)
|
|
352
358
|
}
|
|
353
359
|
|
|
354
360
|
const docRemote = remote.get(storageId as StorageId)
|
|
355
|
-
if (docRemote && docRemote.
|
|
361
|
+
if (docRemote && docRemote.lastSyncTimestamp >= timestamp) {
|
|
356
362
|
continue
|
|
357
363
|
} else {
|
|
358
364
|
remote.set(storageId as StorageId, {
|
|
359
|
-
timestamp,
|
|
360
|
-
|
|
365
|
+
lastSyncTimestamp: timestamp,
|
|
366
|
+
lastHeads: heads as UrlHeads,
|
|
361
367
|
})
|
|
362
368
|
changedHeads.push({
|
|
363
369
|
documentId,
|
|
@@ -370,8 +376,3 @@ export class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSubscripti
|
|
|
370
376
|
return changedHeads
|
|
371
377
|
}
|
|
372
378
|
}
|
|
373
|
-
|
|
374
|
-
type LastHeads = {
|
|
375
|
-
timestamp: number
|
|
376
|
-
heads: UrlHeads
|
|
377
|
-
}
|
package/src/Repo.ts
CHANGED
|
@@ -206,17 +206,17 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
206
206
|
return
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
const heads = handle.
|
|
209
|
+
const heads = handle.getSyncInfo(storageId)?.lastHeads
|
|
210
210
|
const haveHeadsChanged =
|
|
211
211
|
message.syncState.theirHeads &&
|
|
212
212
|
(!heads ||
|
|
213
213
|
!headsAreSame(heads, encodeHeads(message.syncState.theirHeads)))
|
|
214
214
|
|
|
215
215
|
if (haveHeadsChanged && message.syncState.theirHeads) {
|
|
216
|
-
handle.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
)
|
|
216
|
+
handle.setSyncInfo(storageId, {
|
|
217
|
+
lastHeads: encodeHeads(message.syncState.theirHeads),
|
|
218
|
+
lastSyncTimestamp: Date.now(),
|
|
219
|
+
})
|
|
220
220
|
|
|
221
221
|
if (storageId && this.#remoteHeadsGossipingEnabled) {
|
|
222
222
|
this.#remoteHeadsSubscriptions.handleImmediateRemoteHeadsChanged(
|
|
@@ -255,10 +255,16 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
255
255
|
}
|
|
256
256
|
})
|
|
257
257
|
|
|
258
|
-
this.#remoteHeadsSubscriptions.on(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
258
|
+
this.#remoteHeadsSubscriptions.on(
|
|
259
|
+
"remote-heads-changed",
|
|
260
|
+
({ documentId, storageId, remoteHeads, timestamp }) => {
|
|
261
|
+
const handle = this.#handleCache[documentId]
|
|
262
|
+
handle.setSyncInfo(storageId, {
|
|
263
|
+
lastHeads: remoteHeads,
|
|
264
|
+
lastSyncTimestamp: timestamp,
|
|
265
|
+
})
|
|
266
|
+
}
|
|
267
|
+
)
|
|
262
268
|
}
|
|
263
269
|
}
|
|
264
270
|
|
|
@@ -624,6 +630,12 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
624
630
|
}
|
|
625
631
|
// If the handle isn't ready, wait for it and then return it
|
|
626
632
|
await progress.handle.whenReady([READY, UNAVAILABLE])
|
|
633
|
+
if (
|
|
634
|
+
progress.handle.state === "unavailable" &&
|
|
635
|
+
!allowableStates.includes(UNAVAILABLE)
|
|
636
|
+
) {
|
|
637
|
+
throw new Error(`Document ${id} is unavailable`)
|
|
638
|
+
}
|
|
627
639
|
return progress.handle
|
|
628
640
|
}
|
|
629
641
|
}
|
package/src/index.ts
CHANGED
|
@@ -298,6 +298,8 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
298
298
|
endSync(peerId: PeerId) {
|
|
299
299
|
this.#log(`removing peer ${peerId}`)
|
|
300
300
|
this.#peers = this.#peers.filter(p => p !== peerId)
|
|
301
|
+
delete this.#peerDocumentStatuses[peerId]
|
|
302
|
+
this.#checkDocUnavailable()
|
|
301
303
|
}
|
|
302
304
|
|
|
303
305
|
receiveMessage(message: RepoMessage) {
|
|
@@ -399,7 +401,7 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
399
401
|
// if we know none of the peers have the document, tell all our peers that we don't either
|
|
400
402
|
if (
|
|
401
403
|
this.#syncStarted &&
|
|
402
|
-
this.#handle.inState([REQUESTING]) &&
|
|
404
|
+
this.#handle.inState([REQUESTING, UNAVAILABLE]) &&
|
|
403
405
|
this.#peers.every(
|
|
404
406
|
peerId =>
|
|
405
407
|
this.#peerDocumentStatuses[peerId] === "unavailable" ||
|
|
@@ -409,6 +411,9 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
409
411
|
this.#peers
|
|
410
412
|
.filter(peerId => this.#peerDocumentStatuses[peerId] === "wants")
|
|
411
413
|
.forEach(peerId => {
|
|
414
|
+
// Transition the peer to unavailable so that we don't send it a doc-unavailable
|
|
415
|
+
// message every time we run #checkDocUnavailable
|
|
416
|
+
this.#peerDocumentStatuses[peerId] = "unavailable"
|
|
412
417
|
const message: MessageContents<DocumentUnavailableMessage> = {
|
|
413
418
|
type: "doc-unavailable",
|
|
414
419
|
documentId: this.#handle.documentId,
|
|
@@ -3,7 +3,7 @@ import assert from "assert"
|
|
|
3
3
|
import { describe, it } from "vitest"
|
|
4
4
|
import { generateAutomergeUrl, parseAutomergeUrl } from "../src/AutomergeUrl.js"
|
|
5
5
|
import { RemoteHeadsSubscriptions } from "../src/RemoteHeadsSubscriptions.js"
|
|
6
|
-
import { PeerId, StorageId } from "../src/index.js"
|
|
6
|
+
import { PeerId, StorageId, UrlHeads } from "../src/index.js"
|
|
7
7
|
import {
|
|
8
8
|
RemoteHeadsChanged,
|
|
9
9
|
RemoteSubscriptionControlMessage,
|
|
@@ -32,7 +32,7 @@ describe("RepoHeadsSubscriptions", () => {
|
|
|
32
32
|
newHeads: {
|
|
33
33
|
[storageB]: {
|
|
34
34
|
heads: [],
|
|
35
|
-
timestamp:
|
|
35
|
+
timestamp: 1000,
|
|
36
36
|
},
|
|
37
37
|
},
|
|
38
38
|
}
|
|
@@ -45,7 +45,7 @@ describe("RepoHeadsSubscriptions", () => {
|
|
|
45
45
|
newHeads: {
|
|
46
46
|
[storageB]: {
|
|
47
47
|
heads: [],
|
|
48
|
-
timestamp:
|
|
48
|
+
timestamp: 2000,
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
51
|
}
|
|
@@ -64,7 +64,7 @@ describe("RepoHeadsSubscriptions", () => {
|
|
|
64
64
|
newHeads: {
|
|
65
65
|
[storageB]: {
|
|
66
66
|
heads: docBHeads,
|
|
67
|
-
timestamp:
|
|
67
|
+
timestamp: 3000,
|
|
68
68
|
},
|
|
69
69
|
},
|
|
70
70
|
}
|
|
@@ -153,7 +153,7 @@ describe("RepoHeadsSubscriptions", () => {
|
|
|
153
153
|
remoteHeadsSubscriptions.handleImmediateRemoteHeadsChanged(
|
|
154
154
|
docC,
|
|
155
155
|
storageB,
|
|
156
|
-
[]
|
|
156
|
+
[] as UrlHeads
|
|
157
157
|
)
|
|
158
158
|
|
|
159
159
|
// should forward remote-heads events
|
|
@@ -233,7 +233,7 @@ describe("RepoHeadsSubscriptions", () => {
|
|
|
233
233
|
remoteHeadsSubscriptions.handleImmediateRemoteHeadsChanged(
|
|
234
234
|
docC,
|
|
235
235
|
storageB,
|
|
236
|
-
[]
|
|
236
|
+
[] as UrlHeads
|
|
237
237
|
)
|
|
238
238
|
|
|
239
239
|
// expect peer c to be notified both changes
|
|
@@ -279,7 +279,7 @@ describe("RepoHeadsSubscriptions", () => {
|
|
|
279
279
|
remoteHeadsSubscriptions.handleImmediateRemoteHeadsChanged(
|
|
280
280
|
docC,
|
|
281
281
|
storageB,
|
|
282
|
-
[]
|
|
282
|
+
[] as UrlHeads
|
|
283
283
|
)
|
|
284
284
|
|
|
285
285
|
// expect peer c to be notified both changes
|
|
@@ -309,6 +309,7 @@ describe("RepoHeadsSubscriptions", () => {
|
|
|
309
309
|
assert.strictEqual(messages[0].storageId, storageB)
|
|
310
310
|
assert.strictEqual(messages[0].documentId, docB)
|
|
311
311
|
assert.deepStrictEqual(messages[0].remoteHeads, docBHeads)
|
|
312
|
+
assert.strictEqual(messages[0].timestamp, 3000)
|
|
312
313
|
})
|
|
313
314
|
|
|
314
315
|
it("should remove subs of disconnected peers", async () => {
|
package/test/Repo.test.ts
CHANGED
|
@@ -208,6 +208,65 @@ describe("Repo", () => {
|
|
|
208
208
|
}).rejects.toThrow(/Document (.*) is unavailable/)
|
|
209
209
|
})
|
|
210
210
|
|
|
211
|
+
it("immediately marks a document as unavailable even if requested multiple times", async () => {
|
|
212
|
+
/**
|
|
213
|
+
* This exercises an issue where the first time a document is requested
|
|
214
|
+
* from some remote and the remote doesn't have the document then it
|
|
215
|
+
* immediately returns an unavailable error, but if the same document is
|
|
216
|
+
* requested again before the remote is restarted then it never sends
|
|
217
|
+
* the unavailable message leading to timeouts on the requesting end
|
|
218
|
+
*/
|
|
219
|
+
const alice = new Repo({
|
|
220
|
+
peerId: "alice" as PeerId,
|
|
221
|
+
sharePolicy: async () => false,
|
|
222
|
+
})
|
|
223
|
+
const bob = new Repo({ peerId: "bob" as PeerId })
|
|
224
|
+
const [aliceToBob, bobToAlice] = DummyNetworkAdapter.createConnectedPair()
|
|
225
|
+
alice.networkSubsystem.addNetworkAdapter(aliceToBob)
|
|
226
|
+
bob.networkSubsystem.addNetworkAdapter(bobToAlice)
|
|
227
|
+
aliceToBob.peerCandidate("bob" as PeerId)
|
|
228
|
+
bobToAlice.peerCandidate("alice" as PeerId)
|
|
229
|
+
await Promise.all([
|
|
230
|
+
alice.networkSubsystem.whenReady(),
|
|
231
|
+
bob.networkSubsystem.whenReady(),
|
|
232
|
+
])
|
|
233
|
+
|
|
234
|
+
await assert.rejects(() =>
|
|
235
|
+
bob.find("automerge:uKK1dJ4vE3E6r27kz5bsFaCykvM" as AutomergeUrl)
|
|
236
|
+
)
|
|
237
|
+
aliceToBob.emit("peer-disconnected", { peerId: "bob" as PeerId })
|
|
238
|
+
bobToAlice.emit("peer-disconnected", { peerId: "alice" as PeerId })
|
|
239
|
+
|
|
240
|
+
const charlie = new Repo({ peerId: "charlie" as PeerId })
|
|
241
|
+
const [charlieToAlice, aliceToCharlie] =
|
|
242
|
+
DummyNetworkAdapter.createConnectedPair()
|
|
243
|
+
charlie.networkSubsystem.addNetworkAdapter(charlieToAlice)
|
|
244
|
+
alice.networkSubsystem.addNetworkAdapter(aliceToCharlie)
|
|
245
|
+
charlieToAlice.peerCandidate("alice" as PeerId)
|
|
246
|
+
aliceToCharlie.peerCandidate("charlie" as PeerId)
|
|
247
|
+
await Promise.all([
|
|
248
|
+
charlie.networkSubsystem.whenReady(),
|
|
249
|
+
alice.networkSubsystem.whenReady(),
|
|
250
|
+
])
|
|
251
|
+
|
|
252
|
+
await assert.rejects(() =>
|
|
253
|
+
charlie.find("automerge:uKK1dJ4vE3E6r27kz5bsFaCykvM" as AutomergeUrl)
|
|
254
|
+
)
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it("should not return an unavailable handle on second request", async () => {
|
|
258
|
+
const alice = new Repo({
|
|
259
|
+
peerId: "alice" as PeerId,
|
|
260
|
+
sharePolicy: async () => true,
|
|
261
|
+
})
|
|
262
|
+
await assert.rejects(() =>
|
|
263
|
+
alice.find("automerge:uKK1dJ4vE3E6r27kz5bsFaCykvM" as AutomergeUrl)
|
|
264
|
+
)
|
|
265
|
+
await assert.rejects(() =>
|
|
266
|
+
alice.find("automerge:uKK1dJ4vE3E6r27kz5bsFaCykvM" as AutomergeUrl)
|
|
267
|
+
)
|
|
268
|
+
})
|
|
269
|
+
|
|
211
270
|
it("doesn't mark a document as unavailable until network adapters are ready", async () => {
|
|
212
271
|
const { repo, networkAdapter } = setup({ startReady: false })
|
|
213
272
|
const url = generateAutomergeUrl()
|
|
@@ -1267,9 +1326,10 @@ describe("Repo", () => {
|
|
|
1267
1326
|
const nextRemoteHeadsPromise = new Promise<{
|
|
1268
1327
|
storageId: StorageId
|
|
1269
1328
|
heads: UrlHeads
|
|
1329
|
+
timestamp: number
|
|
1270
1330
|
}>(resolve => {
|
|
1271
|
-
handle.on("remote-heads", ({ storageId, heads }) => {
|
|
1272
|
-
resolve({ storageId, heads })
|
|
1331
|
+
handle.on("remote-heads", ({ storageId, heads, timestamp }) => {
|
|
1332
|
+
resolve({ storageId, heads, timestamp })
|
|
1273
1333
|
})
|
|
1274
1334
|
})
|
|
1275
1335
|
|
|
@@ -1289,10 +1349,10 @@ describe("Repo", () => {
|
|
|
1289
1349
|
assert.deepStrictEqual(nextRemoteHeads.storageId, charliedStorageId)
|
|
1290
1350
|
assert.deepStrictEqual(nextRemoteHeads.heads, charlieHandle.heads())
|
|
1291
1351
|
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
)
|
|
1352
|
+
const syncInfo = handle.getSyncInfo(charliedStorageId)
|
|
1353
|
+
|
|
1354
|
+
assert.deepStrictEqual(syncInfo?.lastHeads, charlieHandle.heads())
|
|
1355
|
+
assert.strictEqual(syncInfo?.lastSyncTimestamp, nextRemoteHeads.timestamp)
|
|
1296
1356
|
|
|
1297
1357
|
teardown()
|
|
1298
1358
|
})
|
package/test/remoteHeads.test.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
DocHandleRemoteHeadsPayload,
|
|
10
10
|
PeerId,
|
|
11
11
|
Repo,
|
|
12
|
+
UrlHeads,
|
|
12
13
|
} from "../src/index.js"
|
|
13
14
|
import { DummyStorageAdapter } from "../src/helpers/DummyStorageAdapter.js"
|
|
14
15
|
import { collectMessages } from "./helpers/collectMessages.js"
|
|
@@ -18,7 +19,7 @@ import { pause } from "../src/helpers/pause.js"
|
|
|
18
19
|
describe("DocHandle.remoteHeads", () => {
|
|
19
20
|
const TEST_ID = parseAutomergeUrl(generateAutomergeUrl()).documentId
|
|
20
21
|
|
|
21
|
-
it("should allow to listen for remote head changes and manually read
|
|
22
|
+
it("should allow to listen for remote head changes and manually read sync info", async () => {
|
|
22
23
|
const handle = new DocHandle<TestDoc>(TEST_ID, { isNew: true })
|
|
23
24
|
const bobRepo = new Repo({
|
|
24
25
|
peerId: "bob" as PeerId,
|
|
@@ -29,7 +30,10 @@ describe("DocHandle.remoteHeads", () => {
|
|
|
29
30
|
const bobStorageId = await bobRepo.storageId()
|
|
30
31
|
|
|
31
32
|
const remoteHeadsMessagePromise = eventPromise(handle, "remote-heads")
|
|
32
|
-
handle.
|
|
33
|
+
handle.setSyncInfo(bobStorageId, {
|
|
34
|
+
lastHeads: [] as UrlHeads,
|
|
35
|
+
lastSyncTimestamp: 1000,
|
|
36
|
+
})
|
|
33
37
|
|
|
34
38
|
const remoteHeadsMessage = await remoteHeadsMessagePromise
|
|
35
39
|
|
|
@@ -37,7 +41,13 @@ describe("DocHandle.remoteHeads", () => {
|
|
|
37
41
|
assert.deepStrictEqual(remoteHeadsMessage.heads, [])
|
|
38
42
|
|
|
39
43
|
// read remote heads manually
|
|
40
|
-
|
|
44
|
+
|
|
45
|
+
const syncInfo = handle.getSyncInfo(bobStorageId)
|
|
46
|
+
|
|
47
|
+
assert.deepStrictEqual(syncInfo, {
|
|
48
|
+
lastHeads: [] as UrlHeads,
|
|
49
|
+
lastSyncTimestamp: 1000,
|
|
50
|
+
})
|
|
41
51
|
})
|
|
42
52
|
|
|
43
53
|
describe("multi hop sync", () => {
|