@automerge/automerge-repo 1.1.0-alpha.6 → 1.1.0-alpha.7
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/README.md +10 -7
- package/dist/AutomergeUrl.js +1 -1
- package/dist/RemoteHeadsSubscriptions.js +3 -3
- package/dist/Repo.d.ts +5 -0
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +20 -3
- package/dist/helpers/debounce.js +1 -1
- package/dist/helpers/throttle.js +1 -1
- package/dist/network/NetworkAdapter.js +1 -1
- package/dist/network/NetworkSubsystem.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.js +2 -0
- package/dist/storage/StorageSubsystem.js +1 -1
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +4 -0
- package/package.json +4 -3
- package/src/AutomergeUrl.ts +1 -1
- package/src/RemoteHeadsSubscriptions.ts +3 -3
- package/src/Repo.ts +22 -2
- package/src/helpers/debounce.ts +1 -1
- package/src/helpers/throttle.ts +1 -1
- package/src/network/NetworkAdapter.ts +1 -1
- package/src/network/NetworkSubsystem.ts +2 -1
- package/src/storage/StorageSubsystem.ts +1 -1
- package/src/synchronizer/CollectionSynchronizer.ts +1 -1
- package/src/synchronizer/DocSynchronizer.ts +4 -0
- package/test/RemoteHeadsSubscriptions.test.ts +4 -1
- package/test/Repo.test.ts +25 -1
- package/test/remoteHeads.test.ts +3 -5
package/README.md
CHANGED
|
@@ -45,6 +45,8 @@ A `Repo` exposes these methods:
|
|
|
45
45
|
networks.
|
|
46
46
|
- `delete(docId: DocumentId)`
|
|
47
47
|
Deletes the local copy of a document from the local cache and local storage. _This does not currently delete the document from any other peers_.
|
|
48
|
+
- `import(binary: Uint8Array)`
|
|
49
|
+
Imports a document binary (from `Automerge.save(doc)`) into the repo, returning a new handle
|
|
48
50
|
- `.on("document", ({handle: DocHandle}) => void)`
|
|
49
51
|
Registers a callback to be fired each time a new document is loaded or created.
|
|
50
52
|
- `.on("delete-document", ({handle: DocHandle}) => void)`
|
|
@@ -64,7 +66,7 @@ the document.
|
|
|
64
66
|
|
|
65
67
|
A `DocHandle` also emits these events:
|
|
66
68
|
|
|
67
|
-
- `change({handle: DocHandle, patches: Patch[], patchInfo: PatchInfo})`
|
|
69
|
+
- `change({handle: DocHandle, patches: Patch[], patchInfo: PatchInfo})`
|
|
68
70
|
Called whenever the document changes, the handle's .doc
|
|
69
71
|
- `delete`
|
|
70
72
|
Called when the document is deleted locally.
|
|
@@ -85,11 +87,12 @@ network adapter:
|
|
|
85
87
|
const repo = new Repo({
|
|
86
88
|
network: [new BroadcastChannelNetworkAdapter()],
|
|
87
89
|
storage: new IndexedDBStorageAdapter(),
|
|
88
|
-
sharePolicy: async (peerId: PeerId, documentId: DocumentId) => true // this is the default
|
|
90
|
+
sharePolicy: async (peerId: PeerId, documentId: DocumentId) => true, // this is the default
|
|
89
91
|
})
|
|
90
92
|
```
|
|
91
93
|
|
|
92
94
|
### Share Policy
|
|
95
|
+
|
|
93
96
|
The share policy is used to determine which document in your repo should be _automatically_ shared with other peers. **The default setting is to share all documents with all peers.**
|
|
94
97
|
|
|
95
98
|
> **Warning**
|
|
@@ -99,7 +102,6 @@ You can override this by providing a custom share policy. The function should re
|
|
|
99
102
|
|
|
100
103
|
The share policy will not stop a document being _requested_ by another peer by its `DocumentId`.
|
|
101
104
|
|
|
102
|
-
```ts
|
|
103
105
|
## Starting the demo app
|
|
104
106
|
|
|
105
107
|
```bash
|
|
@@ -272,7 +274,8 @@ you'll need to manually copy the `rootDocId` value between the browsers.)
|
|
|
272
274
|
Originally authored by Peter van Hardenberg.
|
|
273
275
|
|
|
274
276
|
With gratitude for contributions by:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
277
|
+
|
|
278
|
+
- Herb Caudill
|
|
279
|
+
- Jeremy Rose
|
|
280
|
+
- Alex Currie-Clark
|
|
281
|
+
- Dylan Mackenzie
|
package/dist/AutomergeUrl.js
CHANGED
|
@@ -21,7 +21,7 @@ export const parseAutomergeUrl = (url) => {
|
|
|
21
21
|
* Throws on invalid input.
|
|
22
22
|
*/
|
|
23
23
|
export const stringifyAutomergeUrl = (arg) => {
|
|
24
|
-
|
|
24
|
+
const documentId = arg instanceof Uint8Array || typeof arg === "string"
|
|
25
25
|
? arg
|
|
26
26
|
: "documentId" in arg
|
|
27
27
|
? arg.documentId
|
|
@@ -248,7 +248,7 @@ export class RemoteHeadsSubscriptions extends EventEmitter {
|
|
|
248
248
|
}
|
|
249
249
|
}
|
|
250
250
|
#isPeerSubscribedToDoc(peerId, documentId) {
|
|
251
|
-
|
|
251
|
+
const subscribedDocs = this.#subscribedDocsByPeer.get(peerId);
|
|
252
252
|
return subscribedDocs && subscribedDocs.has(documentId);
|
|
253
253
|
}
|
|
254
254
|
/** Returns the (document, storageId) pairs which have changed after processing msg */
|
|
@@ -262,11 +262,11 @@ export class RemoteHeadsSubscriptions extends EventEmitter {
|
|
|
262
262
|
}
|
|
263
263
|
let remote = this.#knownHeads.get(documentId);
|
|
264
264
|
if (!remote) {
|
|
265
|
-
remote = new Map(
|
|
265
|
+
remote = new Map();
|
|
266
266
|
this.#knownHeads.set(documentId, remote);
|
|
267
267
|
}
|
|
268
268
|
const docRemote = remote.get(storageId);
|
|
269
|
-
if (docRemote && docRemote.timestamp
|
|
269
|
+
if (docRemote && docRemote.timestamp >= timestamp) {
|
|
270
270
|
continue;
|
|
271
271
|
}
|
|
272
272
|
else {
|
package/dist/Repo.d.ts
CHANGED
|
@@ -67,6 +67,11 @@ export declare class Repo extends EventEmitter<RepoEvents> {
|
|
|
67
67
|
delete(
|
|
68
68
|
/** The url or documentId of the handle to delete */
|
|
69
69
|
id: AnyDocumentId): void;
|
|
70
|
+
/**
|
|
71
|
+
* Imports document binary into the repo.
|
|
72
|
+
* @param binary - The binary to import
|
|
73
|
+
*/
|
|
74
|
+
import<T>(binary: Uint8Array): DocHandle<T>;
|
|
70
75
|
subscribeToRemotes: (remotes: StorageId[]) => void;
|
|
71
76
|
storageId: () => Promise<StorageId | undefined>;
|
|
72
77
|
}
|
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;AAM5C,OAAO,EAAE,SAAS,EAAiC,MAAM,gBAAgB,CAAA;AAEzE,OAAO,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAEhE,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAEnE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAI9C,8FAA8F;AAC9F;;;;;;GAMG;AACH,qBAAa,IAAK,SAAQ,YAAY,CAAC,UAAU,CAAC;;IAGhD,cAAc;IACd,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,cAAc;IACd,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IAEnC,mDAAmD;IACnD,cAAc;IACd,gBAAgB,SAAM;IAMtB,sDAAsD;IACtD,cAAc;IACd,WAAW,EAAE,WAAW,CAAmB;IAE3C,8GAA8G;IAC9G,cAAc;IACd,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAK;gBAI3C,EACV,OAAO,EACP,OAAO,EACP,MAAM,EACN,WAAW,EACX,WAAmC,GACpC,EAAE,UAAU;
|
|
1
|
+
{"version":3,"file":"Repo.d.ts","sourceRoot":"","sources":["../src/Repo.ts"],"names":[],"mappings":"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"}
|
package/dist/Repo.js
CHANGED
|
@@ -48,7 +48,7 @@ export class Repo extends EventEmitter {
|
|
|
48
48
|
const saveFn = ({ handle, doc, }) => {
|
|
49
49
|
void storageSubsystem.saveDoc(handle.documentId, doc);
|
|
50
50
|
};
|
|
51
|
-
|
|
51
|
+
handle.on("heads-changed", throttle(saveFn, this.saveDebounceRate));
|
|
52
52
|
if (isNew) {
|
|
53
53
|
// this is a new document, immediately save it
|
|
54
54
|
await storageSubsystem.saveDoc(handle.documentId, handle.docSync());
|
|
@@ -110,7 +110,9 @@ export class Repo extends EventEmitter {
|
|
|
110
110
|
this.storageSubsystem = storageSubsystem;
|
|
111
111
|
// NETWORK
|
|
112
112
|
// The network subsystem deals with sending and receiving messages to and from peers.
|
|
113
|
-
const myPeerMetadata = new Promise(
|
|
113
|
+
const myPeerMetadata = new Promise(
|
|
114
|
+
// eslint-disable-next-line no-async-promise-executor -- TODO: fix
|
|
115
|
+
async (resolve) => resolve({
|
|
114
116
|
storageId: await storageSubsystem?.id(),
|
|
115
117
|
isEphemeral,
|
|
116
118
|
}));
|
|
@@ -218,7 +220,10 @@ export class Repo extends EventEmitter {
|
|
|
218
220
|
let handler = this.#throttledSaveSyncStateHandlers[storageId];
|
|
219
221
|
if (!handler) {
|
|
220
222
|
handler = this.#throttledSaveSyncStateHandlers[storageId] = throttle(({ documentId, syncState }) => {
|
|
221
|
-
this.storageSubsystem.saveSyncState(documentId, storageId, syncState)
|
|
223
|
+
this.storageSubsystem.saveSyncState(documentId, storageId, syncState)
|
|
224
|
+
.catch(err => {
|
|
225
|
+
this.#log("error saving sync state", { err });
|
|
226
|
+
});
|
|
222
227
|
}, this.saveDebounceRate);
|
|
223
228
|
}
|
|
224
229
|
handler(message);
|
|
@@ -340,6 +345,18 @@ export class Repo extends EventEmitter {
|
|
|
340
345
|
delete this.#handleCache[documentId];
|
|
341
346
|
this.emit("delete-document", { documentId });
|
|
342
347
|
}
|
|
348
|
+
/**
|
|
349
|
+
* Imports document binary into the repo.
|
|
350
|
+
* @param binary - The binary to import
|
|
351
|
+
*/
|
|
352
|
+
import(binary) {
|
|
353
|
+
const doc = Automerge.load(binary);
|
|
354
|
+
const handle = this.create();
|
|
355
|
+
handle.update(() => {
|
|
356
|
+
return Automerge.clone(doc);
|
|
357
|
+
});
|
|
358
|
+
return handle;
|
|
359
|
+
}
|
|
343
360
|
subscribeToRemotes = (remotes) => {
|
|
344
361
|
this.#log("subscribeToRemotes", { remotes });
|
|
345
362
|
this.#remoteHeadsSubscriptions.subscribeToRemotes(remotes);
|
package/dist/helpers/debounce.js
CHANGED
package/dist/helpers/throttle.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkSubsystem.d.ts","sourceRoot":"","sources":["../../src/network/NetworkSubsystem.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAa,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EACV,cAAc,EACd,uBAAuB,EACvB,YAAY,EACb,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAEL,eAAe,EACf,WAAW,EAGZ,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"NetworkSubsystem.d.ts","sourceRoot":"","sources":["../../src/network/NetworkSubsystem.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAa,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EACV,cAAc,EACd,uBAAuB,EACvB,YAAY,EACb,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAEL,eAAe,EACf,WAAW,EAGZ,MAAM,eAAe,CAAA;AAOtB,qBAAa,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;;IAY/D,MAAM;IACb,OAAO,CAAC,YAAY;gBAFpB,QAAQ,EAAE,cAAc,EAAE,EACnB,MAAM,QAAiB,EACtB,YAAY,EAAE,OAAO,CAAC,YAAY,CAAC;IAO7C,iBAAiB,CAAC,cAAc,EAAE,cAAc;IAyEhD,IAAI,CAAC,OAAO,EAAE,eAAe;IAsC7B,OAAO,gBAEN;IAED,SAAS,sBAUR;CACF;AAQD,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;IACpC,mBAAmB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAC/D,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;IACvC,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,YAAY,CAAA;CAC3B"}
|
|
@@ -23,7 +23,7 @@ export class StorageSubsystem {
|
|
|
23
23
|
this.#storageAdapter = storageAdapter;
|
|
24
24
|
}
|
|
25
25
|
async id() {
|
|
26
|
-
|
|
26
|
+
const storedId = await this.#storageAdapter.load(["storage-adapter-id"]);
|
|
27
27
|
let id;
|
|
28
28
|
if (storedId) {
|
|
29
29
|
id = new TextDecoder().decode(storedId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CollectionSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/CollectionSynchronizer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"CollectionSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/CollectionSynchronizer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIhD,4FAA4F;AAC5F,qBAAa,sBAAuB,SAAQ,YAAY;;IAU1C,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,IAAI;IAqD9B;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU;IAyBxC;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,UAAU;IAYlC,cAAc,CAAC,UAAU,EAAE,UAAU;IAIrC,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;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/DocSynchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAG9C,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,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,CAAC,CAAC,SAAS,GAAG,SAAS,CAAA;CAC9D;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;;IAE/C,gBAAgB,SAAM;gBAsBV,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,qBAAqB;IAyB9D,IAAI,UAAU,uCAEb;IAED,IAAI,UAAU,qCAEb;
|
|
1
|
+
{"version":3,"file":"DocSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/DocSynchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAG9C,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,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,CAAC,CAAC,SAAS,GAAG,SAAS,CAAA;CAC9D;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;;IAE/C,gBAAgB,SAAM;gBAsBV,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,qBAAqB;IAyB9D,IAAI,UAAU,uCAEb;IAED,IAAI,UAAU,qCAEb;IAgID,OAAO,CAAC,MAAM,EAAE,MAAM;IAItB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IAiD3B,OAAO,CAAC,MAAM,EAAE,MAAM;IAKtB,cAAc,CAAC,OAAO,EAAE,WAAW;IAkBnC,uBAAuB,CAAC,OAAO,EAAE,gBAAgB;IAuBjD,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc;CA8EzD"}
|
|
@@ -79,6 +79,8 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
79
79
|
if (!pendingCallbacks) {
|
|
80
80
|
this.#onLoadSyncState(peerId).then(syncState => {
|
|
81
81
|
this.#initSyncState(peerId, syncState ?? A.initSyncState());
|
|
82
|
+
}).catch(err => {
|
|
83
|
+
this.#log(`Error loading sync state for ${peerId}: ${err}`);
|
|
82
84
|
});
|
|
83
85
|
pendingCallbacks = this.#pendingSyncStateCallbacks[peerId] = [];
|
|
84
86
|
}
|
|
@@ -178,6 +180,8 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
178
180
|
if (doc) {
|
|
179
181
|
this.#sendSyncMessage(peerId, doc);
|
|
180
182
|
}
|
|
183
|
+
}).catch(err => {
|
|
184
|
+
this.#log(`Error loading doc for ${peerId}: ${err}`);
|
|
181
185
|
});
|
|
182
186
|
});
|
|
183
187
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo",
|
|
3
|
-
"version": "1.1.0-alpha.
|
|
3
|
+
"version": "1.1.0-alpha.7",
|
|
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>",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc",
|
|
12
|
+
"lint": "eslint --ext .ts src",
|
|
12
13
|
"watch": "npm-watch build",
|
|
13
14
|
"test:coverage": "c8 --reporter=lcov --reporter=html --reporter=text yarn test",
|
|
14
15
|
"test": "vitest",
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"http-server": "^14.1.0",
|
|
23
|
-
"vite": "^
|
|
24
|
+
"vite": "^5.0.8"
|
|
24
25
|
},
|
|
25
26
|
"dependencies": {
|
|
26
27
|
"@automerge/automerge": "^2.1.9",
|
|
@@ -55,5 +56,5 @@
|
|
|
55
56
|
"publishConfig": {
|
|
56
57
|
"access": "public"
|
|
57
58
|
},
|
|
58
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "9a4711e39c93273d992c5686257246ddfaaafddd"
|
|
59
60
|
}
|
package/src/AutomergeUrl.ts
CHANGED
|
@@ -33,7 +33,7 @@ export const parseAutomergeUrl = (url: AutomergeUrl) => {
|
|
|
33
33
|
export const stringifyAutomergeUrl = (
|
|
34
34
|
arg: UrlOptions | DocumentId | BinaryDocumentId
|
|
35
35
|
) => {
|
|
36
|
-
|
|
36
|
+
const documentId =
|
|
37
37
|
arg instanceof Uint8Array || typeof arg === "string"
|
|
38
38
|
? arg
|
|
39
39
|
: "documentId" in arg
|
|
@@ -326,7 +326,7 @@ export class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSubscripti
|
|
|
326
326
|
}
|
|
327
327
|
|
|
328
328
|
#isPeerSubscribedToDoc(peerId: PeerId, documentId: DocumentId) {
|
|
329
|
-
|
|
329
|
+
const subscribedDocs = this.#subscribedDocsByPeer.get(peerId)
|
|
330
330
|
return subscribedDocs && subscribedDocs.has(documentId)
|
|
331
331
|
}
|
|
332
332
|
|
|
@@ -348,12 +348,12 @@ export class RemoteHeadsSubscriptions extends EventEmitter<RemoteHeadsSubscripti
|
|
|
348
348
|
}
|
|
349
349
|
let remote = this.#knownHeads.get(documentId)
|
|
350
350
|
if (!remote) {
|
|
351
|
-
remote = new Map(
|
|
351
|
+
remote = new Map()
|
|
352
352
|
this.#knownHeads.set(documentId, remote)
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
const docRemote = remote.get(storageId as StorageId)
|
|
356
|
-
if (docRemote && docRemote.timestamp
|
|
356
|
+
if (docRemote && docRemote.timestamp >= timestamp) {
|
|
357
357
|
continue
|
|
358
358
|
} else {
|
|
359
359
|
remote.set(storageId as StorageId, { timestamp, heads })
|
package/src/Repo.ts
CHANGED
|
@@ -77,7 +77,7 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
77
77
|
}: DocHandleEncodedChangePayload<any>) => {
|
|
78
78
|
void storageSubsystem.saveDoc(handle.documentId, doc)
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
handle.on(
|
|
81
81
|
"heads-changed",
|
|
82
82
|
throttle(saveFn, this.saveDebounceRate)
|
|
83
83
|
)
|
|
@@ -153,7 +153,8 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
153
153
|
// The network subsystem deals with sending and receiving messages to and from peers.
|
|
154
154
|
|
|
155
155
|
const myPeerMetadata: Promise<PeerMetadata> = new Promise(
|
|
156
|
-
async
|
|
156
|
+
// eslint-disable-next-line no-async-promise-executor -- TODO: fix
|
|
157
|
+
async (resolve) =>
|
|
157
158
|
resolve({
|
|
158
159
|
storageId: await storageSubsystem?.id(),
|
|
159
160
|
isEphemeral,
|
|
@@ -300,6 +301,9 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
300
301
|
handler = this.#throttledSaveSyncStateHandlers[storageId] = throttle(
|
|
301
302
|
({ documentId, syncState }: SyncStateMessage) => {
|
|
302
303
|
this.storageSubsystem!.saveSyncState(documentId, storageId, syncState)
|
|
304
|
+
.catch(err => {
|
|
305
|
+
this.#log("error saving sync state", { err })
|
|
306
|
+
})
|
|
303
307
|
},
|
|
304
308
|
this.saveDebounceRate
|
|
305
309
|
)
|
|
@@ -450,6 +454,22 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
450
454
|
this.emit("delete-document", { documentId })
|
|
451
455
|
}
|
|
452
456
|
|
|
457
|
+
/**
|
|
458
|
+
* Imports document binary into the repo.
|
|
459
|
+
* @param binary - The binary to import
|
|
460
|
+
*/
|
|
461
|
+
import<T>(binary: Uint8Array) {
|
|
462
|
+
const doc = Automerge.load<T>(binary)
|
|
463
|
+
|
|
464
|
+
const handle = this.create<T>()
|
|
465
|
+
|
|
466
|
+
handle.update(() => {
|
|
467
|
+
return Automerge.clone(doc)
|
|
468
|
+
})
|
|
469
|
+
|
|
470
|
+
return handle
|
|
471
|
+
}
|
|
472
|
+
|
|
453
473
|
subscribeToRemotes = (remotes: StorageId[]) => {
|
|
454
474
|
this.#log("subscribeToRemotes", { remotes })
|
|
455
475
|
this.#remoteHeadsSubscriptions.subscribeToRemotes(remotes)
|
package/src/helpers/debounce.ts
CHANGED
package/src/helpers/throttle.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface PeerMetadata {
|
|
|
22
22
|
* until the adapter emits a `ready` event before it starts trying to use it
|
|
23
23
|
*/
|
|
24
24
|
export abstract class NetworkAdapter extends EventEmitter<NetworkAdapterEvents> {
|
|
25
|
-
peerId?: PeerId
|
|
25
|
+
peerId?: PeerId
|
|
26
26
|
peerMetadata?: PeerMetadata
|
|
27
27
|
|
|
28
28
|
/** Called by the {@link Repo} to start the connection process
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
isEphemeralMessage,
|
|
14
14
|
isValidRepoMessage,
|
|
15
15
|
} from "./messages.js"
|
|
16
|
-
import { StorageId } from "../storage/types.js"
|
|
17
16
|
|
|
18
17
|
type EphemeralMessageSource = `${PeerId}:${SessionId}`
|
|
19
18
|
|
|
@@ -108,6 +107,8 @@ export class NetworkSubsystem extends EventEmitter<NetworkSubsystemEvents> {
|
|
|
108
107
|
|
|
109
108
|
this.peerMetadata.then(peerMetadata => {
|
|
110
109
|
networkAdapter.connect(this.peerId, peerMetadata)
|
|
110
|
+
}).catch(err => {
|
|
111
|
+
this.#log("error connecting to network", err)
|
|
111
112
|
})
|
|
112
113
|
}
|
|
113
114
|
|
|
@@ -33,7 +33,7 @@ export class StorageSubsystem {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
async id(): Promise<StorageId> {
|
|
36
|
-
|
|
36
|
+
const storedId = await this.#storageAdapter.load(["storage-adapter-id"])
|
|
37
37
|
|
|
38
38
|
let id: StorageId
|
|
39
39
|
if (storedId) {
|
|
@@ -2,7 +2,7 @@ import debug from "debug"
|
|
|
2
2
|
import { DocHandle } from "../DocHandle.js"
|
|
3
3
|
import { stringifyAutomergeUrl } from "../AutomergeUrl.js"
|
|
4
4
|
import { Repo } from "../Repo.js"
|
|
5
|
-
import { DocMessage
|
|
5
|
+
import { DocMessage } from "../network/messages.js"
|
|
6
6
|
import { DocumentId, PeerId } from "../types.js"
|
|
7
7
|
import { DocSynchronizer } from "./DocSynchronizer.js"
|
|
8
8
|
import { Synchronizer } from "./Synchronizer.js"
|
|
@@ -139,6 +139,8 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
139
139
|
if (!pendingCallbacks) {
|
|
140
140
|
this.#onLoadSyncState(peerId).then(syncState => {
|
|
141
141
|
this.#initSyncState(peerId, syncState ?? A.initSyncState())
|
|
142
|
+
}).catch(err => {
|
|
143
|
+
this.#log(`Error loading sync state for ${peerId}: ${err}`)
|
|
142
144
|
})
|
|
143
145
|
pendingCallbacks = this.#pendingSyncStateCallbacks[peerId] = []
|
|
144
146
|
}
|
|
@@ -264,6 +266,8 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
264
266
|
if (doc) {
|
|
265
267
|
this.#sendSyncMessage(peerId, doc)
|
|
266
268
|
}
|
|
269
|
+
}).catch(err => {
|
|
270
|
+
this.#log(`Error loading doc for ${peerId}: ${err}`)
|
|
267
271
|
})
|
|
268
272
|
})
|
|
269
273
|
})
|
|
@@ -287,7 +287,7 @@ describe("RepoHeadsSubscriptions", () => {
|
|
|
287
287
|
assert.strictEqual(messages.length, 0)
|
|
288
288
|
})
|
|
289
289
|
|
|
290
|
-
it("should
|
|
290
|
+
it("should only notify of sync states with a more recent timestamp", async () => {
|
|
291
291
|
const remoteHeadsSubscription = new RemoteHeadsSubscriptions()
|
|
292
292
|
|
|
293
293
|
const messagesPromise = waitForMessages(
|
|
@@ -298,6 +298,9 @@ describe("RepoHeadsSubscriptions", () => {
|
|
|
298
298
|
remoteHeadsSubscription.subscribeToRemotes([storageB])
|
|
299
299
|
remoteHeadsSubscription.handleRemoteHeads(docBHeadsChangedForStorageB2)
|
|
300
300
|
|
|
301
|
+
// send same message
|
|
302
|
+
remoteHeadsSubscription.handleRemoteHeads(docBHeadsChangedForStorageB2)
|
|
303
|
+
|
|
301
304
|
// send message with old heads
|
|
302
305
|
remoteHeadsSubscription.handleRemoteHeads(docBHeadsChangedForStorageB)
|
|
303
306
|
|
package/test/Repo.test.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { next as A } from "@automerge/automerge"
|
|
|
2
2
|
import { MessageChannelNetworkAdapter } from "@automerge/automerge-repo-network-messagechannel"
|
|
3
3
|
import assert from "assert"
|
|
4
4
|
import * as Uuid from "uuid"
|
|
5
|
-
import { describe, it } from "vitest"
|
|
5
|
+
import { describe, expect, it } from "vitest"
|
|
6
6
|
import { READY } from "../src/DocHandle.js"
|
|
7
7
|
import { parseAutomergeUrl } from "../src/AutomergeUrl.js"
|
|
8
8
|
import {
|
|
@@ -396,6 +396,30 @@ describe("Repo", () => {
|
|
|
396
396
|
const storageKeyTypes = storageAdapter.keys().map(k => k.split(".")[1])
|
|
397
397
|
assert(storageKeyTypes.filter(k => k === "snapshot").length === 1)
|
|
398
398
|
})
|
|
399
|
+
|
|
400
|
+
it("can import an existing document", async () => {
|
|
401
|
+
const { repo } = setup()
|
|
402
|
+
const doc = A.init<TestDoc>()
|
|
403
|
+
const updatedDoc = A.change(doc, d => {
|
|
404
|
+
d.foo = "bar"
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
const saved = A.save(updatedDoc)
|
|
408
|
+
|
|
409
|
+
const handle = repo.import<TestDoc>(saved)
|
|
410
|
+
assert.equal(handle.isReady(), true)
|
|
411
|
+
const v = await handle.doc()
|
|
412
|
+
assert.equal(v?.foo, "bar")
|
|
413
|
+
|
|
414
|
+
expect(A.getHistory(v)).toEqual(A.getHistory(updatedDoc))
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
it("throws an error if we try to import an invalid document", async () => {
|
|
418
|
+
const { repo } = setup()
|
|
419
|
+
expect(() => {
|
|
420
|
+
repo.import<TestDoc>(A.init<TestDoc> as unknown as Uint8Array)
|
|
421
|
+
}).toThrow()
|
|
422
|
+
})
|
|
399
423
|
})
|
|
400
424
|
|
|
401
425
|
describe("with peers (linear network)", async () => {
|
package/test/remoteHeads.test.ts
CHANGED
|
@@ -165,11 +165,9 @@ describe("DocHandle.remoteHeads", () => {
|
|
|
165
165
|
).filter(({ type }) => type === "remote-heads-changed")
|
|
166
166
|
|
|
167
167
|
// we should only be notified of the head changes of doc A
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
leftTabDocA.documentId
|
|
172
|
-
)
|
|
168
|
+
const docIds = remoteHeadsChangedMessages.map(d => d.documentId)
|
|
169
|
+
const uniqueDocIds = [...new Set(docIds)]
|
|
170
|
+
assert.deepStrictEqual(uniqueDocIds, [leftTabDocA.documentId])
|
|
173
171
|
})
|
|
174
172
|
|
|
175
173
|
it("should report remote heads for doc on subscribe if peer already knows them", async () => {
|