@automerge/automerge-repo 1.0.19 → 1.1.0-alpha.2
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 +6 -5
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +7 -7
- package/dist/RemoteHeadsSubscriptions.d.ts +41 -0
- package/dist/RemoteHeadsSubscriptions.d.ts.map +1 -0
- package/dist/RemoteHeadsSubscriptions.js +224 -0
- package/dist/Repo.d.ts +11 -2
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +117 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/network/NetworkAdapter.d.ts +15 -1
- package/dist/network/NetworkAdapter.d.ts.map +1 -1
- package/dist/network/NetworkAdapter.js +1 -0
- package/dist/network/NetworkSubsystem.d.ts +4 -2
- package/dist/network/NetworkSubsystem.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.js +8 -4
- package/dist/network/messages.d.ts +24 -1
- package/dist/network/messages.d.ts.map +1 -1
- package/dist/network/messages.js +5 -1
- package/dist/storage/StorageSubsystem.d.ts +5 -3
- package/dist/storage/StorageSubsystem.d.ts.map +1 -1
- package/dist/storage/StorageSubsystem.js +23 -5
- package/dist/storage/types.d.ts +4 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.d.ts +2 -2
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +7 -3
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +0 -9
- package/package.json +3 -3
- package/src/DocHandle.ts +10 -9
- package/src/RemoteHeadsSubscriptions.ts +306 -0
- package/src/Repo.ts +172 -12
- package/src/index.ts +2 -0
- package/src/network/NetworkAdapter.ts +19 -1
- package/src/network/NetworkSubsystem.ts +17 -6
- package/src/network/messages.ts +30 -1
- package/src/storage/StorageSubsystem.ts +30 -7
- package/src/storage/types.ts +3 -0
- package/src/synchronizer/CollectionSynchronizer.ts +11 -5
- package/src/synchronizer/DocSynchronizer.ts +0 -12
- package/test/DocHandle.test.ts +0 -17
- package/test/RemoteHeadsSubscriptions.test.ts +343 -0
- package/test/Repo.test.ts +51 -15
- package/test/StorageSubsystem.test.ts +28 -6
- package/test/remoteHeads.test.ts +135 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { EventEmitter } from "eventemitter3";
|
|
2
2
|
import { PeerId } from "../types.js";
|
|
3
|
-
import { NetworkAdapter, PeerDisconnectedPayload } from "./NetworkAdapter.js";
|
|
3
|
+
import type { NetworkAdapter, PeerDisconnectedPayload, PeerMetadata } from "./NetworkAdapter.js";
|
|
4
4
|
import { MessageContents, RepoMessage } from "./messages.js";
|
|
5
5
|
export declare class NetworkSubsystem extends EventEmitter<NetworkSubsystemEvents> {
|
|
6
6
|
#private;
|
|
7
7
|
peerId: PeerId;
|
|
8
|
-
|
|
8
|
+
private peerMetadata;
|
|
9
|
+
constructor(adapters: NetworkAdapter[], peerId: PeerId, peerMetadata: Promise<PeerMetadata>);
|
|
9
10
|
addNetworkAdapter(networkAdapter: NetworkAdapter): void;
|
|
10
11
|
send(message: MessageContents): void;
|
|
11
12
|
isReady: () => boolean;
|
|
@@ -19,5 +20,6 @@ export interface NetworkSubsystemEvents {
|
|
|
19
20
|
}
|
|
20
21
|
export interface PeerPayload {
|
|
21
22
|
peerId: PeerId;
|
|
23
|
+
peerMetadata: PeerMetadata;
|
|
22
24
|
}
|
|
23
25
|
//# sourceMappingURL=NetworkSubsystem.d.ts.map
|
|
@@ -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,
|
|
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;AAQtB,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;IAuEhD,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"}
|
|
@@ -4,6 +4,7 @@ import { isEphemeralMessage, isValidRepoMessage, } from "./messages.js";
|
|
|
4
4
|
const getEphemeralMessageSource = (message) => `${message.senderId}:${message.sessionId}`;
|
|
5
5
|
export class NetworkSubsystem extends EventEmitter {
|
|
6
6
|
peerId;
|
|
7
|
+
peerMetadata;
|
|
7
8
|
#log;
|
|
8
9
|
#adaptersByPeer = {};
|
|
9
10
|
#count = 0;
|
|
@@ -11,9 +12,10 @@ export class NetworkSubsystem extends EventEmitter {
|
|
|
11
12
|
#ephemeralSessionCounts = {};
|
|
12
13
|
#readyAdapterCount = 0;
|
|
13
14
|
#adapters = [];
|
|
14
|
-
constructor(adapters, peerId = randomPeerId()) {
|
|
15
|
+
constructor(adapters, peerId = randomPeerId(), peerMetadata) {
|
|
15
16
|
super();
|
|
16
17
|
this.peerId = peerId;
|
|
18
|
+
this.peerMetadata = peerMetadata;
|
|
17
19
|
this.#log = debug(`automerge-repo:network:${this.peerId}`);
|
|
18
20
|
adapters.forEach(a => this.addNetworkAdapter(a));
|
|
19
21
|
}
|
|
@@ -26,14 +28,14 @@ export class NetworkSubsystem extends EventEmitter {
|
|
|
26
28
|
this.emit("ready");
|
|
27
29
|
}
|
|
28
30
|
});
|
|
29
|
-
networkAdapter.on("peer-candidate", ({ peerId }) => {
|
|
31
|
+
networkAdapter.on("peer-candidate", ({ peerId, peerMetadata }) => {
|
|
30
32
|
this.#log(`peer candidate: ${peerId} `);
|
|
31
33
|
// TODO: This is where authentication would happen
|
|
32
34
|
if (!this.#adaptersByPeer[peerId]) {
|
|
33
35
|
// TODO: handle losing a server here
|
|
34
36
|
this.#adaptersByPeer[peerId] = networkAdapter;
|
|
35
37
|
}
|
|
36
|
-
this.emit("peer", { peerId });
|
|
38
|
+
this.emit("peer", { peerId, peerMetadata });
|
|
37
39
|
});
|
|
38
40
|
networkAdapter.on("peer-disconnected", ({ peerId }) => {
|
|
39
41
|
this.#log(`peer disconnected: ${peerId} `);
|
|
@@ -65,7 +67,9 @@ export class NetworkSubsystem extends EventEmitter {
|
|
|
65
67
|
}
|
|
66
68
|
});
|
|
67
69
|
});
|
|
68
|
-
|
|
70
|
+
this.peerMetadata.then(peerMetadata => {
|
|
71
|
+
networkAdapter.connect(this.peerId, peerMetadata);
|
|
72
|
+
});
|
|
69
73
|
}
|
|
70
74
|
send(message) {
|
|
71
75
|
const peer = this.#adaptersByPeer[message.targetId];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SyncState } from "@automerge/automerge";
|
|
2
2
|
import { DocumentId, PeerId, SessionId } from "../types.js";
|
|
3
|
+
import { StorageId } from "../storage/types.js";
|
|
3
4
|
/**
|
|
4
5
|
* A sync message for a particular document
|
|
5
6
|
*/
|
|
@@ -76,8 +77,28 @@ export type AuthMessage<TPayload = any> = {
|
|
|
76
77
|
/** The payload of the auth message (up to the specific auth provider) */
|
|
77
78
|
payload: TPayload;
|
|
78
79
|
};
|
|
80
|
+
export type RemoteSubscriptionControlMessage = {
|
|
81
|
+
type: "remote-subscription-change";
|
|
82
|
+
senderId: PeerId;
|
|
83
|
+
targetId: PeerId;
|
|
84
|
+
add?: StorageId[];
|
|
85
|
+
remove?: StorageId[];
|
|
86
|
+
};
|
|
87
|
+
export type RemoteHeadsChanged = {
|
|
88
|
+
type: "remote-heads-changed";
|
|
89
|
+
senderId: PeerId;
|
|
90
|
+
targetId: PeerId;
|
|
91
|
+
documentId: DocumentId;
|
|
92
|
+
newHeads: {
|
|
93
|
+
[key: StorageId]: {
|
|
94
|
+
heads: string[];
|
|
95
|
+
timestamp: number;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
};
|
|
79
99
|
/** These are message types that a {@link NetworkAdapter} surfaces to a {@link Repo}. */
|
|
80
|
-
export type RepoMessage = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage;
|
|
100
|
+
export type RepoMessage = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage | RemoteSubscriptionControlMessage | RemoteHeadsChanged;
|
|
101
|
+
export type DocMessage = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage;
|
|
81
102
|
/** These are all the message types that a {@link NetworkAdapter} might see. */
|
|
82
103
|
export type Message = RepoMessage | AuthMessage;
|
|
83
104
|
/**
|
|
@@ -95,4 +116,6 @@ export declare const isDocumentUnavailableMessage: (msg: Message) => msg is Docu
|
|
|
95
116
|
export declare const isRequestMessage: (msg: Message) => msg is RequestMessage;
|
|
96
117
|
export declare const isSyncMessage: (msg: Message) => msg is SyncMessage;
|
|
97
118
|
export declare const isEphemeralMessage: (msg: Message) => msg is EphemeralMessage;
|
|
119
|
+
export declare const isRemoteSubscriptionControlMessage: (msg: Message) => msg is RemoteSubscriptionControlMessage;
|
|
120
|
+
export declare const isRemoteHeadsChanged: (msg: Message) => msg is RemoteHeadsChanged;
|
|
98
121
|
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/network/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/network/messages.ts"],"names":[],"mappings":"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"}
|
package/dist/network/messages.js
CHANGED
|
@@ -5,9 +5,13 @@ export const isValidRepoMessage = (message) => typeof message === "object" &&
|
|
|
5
5
|
(isSyncMessage(message) ||
|
|
6
6
|
isEphemeralMessage(message) ||
|
|
7
7
|
isRequestMessage(message) ||
|
|
8
|
-
isDocumentUnavailableMessage(message)
|
|
8
|
+
isDocumentUnavailableMessage(message) ||
|
|
9
|
+
isRemoteSubscriptionControlMessage(message) ||
|
|
10
|
+
isRemoteHeadsChanged(message));
|
|
9
11
|
// prettier-ignore
|
|
10
12
|
export const isDocumentUnavailableMessage = (msg) => msg.type === "doc-unavailable";
|
|
11
13
|
export const isRequestMessage = (msg) => msg.type === "request";
|
|
12
14
|
export const isSyncMessage = (msg) => msg.type === "sync";
|
|
13
15
|
export const isEphemeralMessage = (msg) => msg.type === "ephemeral";
|
|
16
|
+
export const isRemoteSubscriptionControlMessage = (msg) => msg.type === "remote-subscription-change";
|
|
17
|
+
export const isRemoteHeadsChanged = (msg) => msg.type === "remote-heads-changed";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as A from "@automerge/automerge/next";
|
|
2
|
-
import {
|
|
2
|
+
import { type DocumentId } from "../types.js";
|
|
3
3
|
import { StorageAdapter } from "./StorageAdapter.js";
|
|
4
|
+
import { StorageId } from "./types.js";
|
|
4
5
|
/**
|
|
5
6
|
* The storage subsystem is responsible for saving and loading Automerge documents to and from
|
|
6
7
|
* storage adapter. It also provides a generic key/value storage interface for other uses.
|
|
@@ -8,6 +9,7 @@ import { StorageAdapter } from "./StorageAdapter.js";
|
|
|
8
9
|
export declare class StorageSubsystem {
|
|
9
10
|
#private;
|
|
10
11
|
constructor(storageAdapter: StorageAdapter);
|
|
12
|
+
id(): Promise<StorageId>;
|
|
11
13
|
/** Loads a value from storage. */
|
|
12
14
|
load(
|
|
13
15
|
/** Namespace to prevent collisions with other users of the storage subsystem. */
|
|
@@ -44,7 +46,7 @@ export declare class StorageSubsystem {
|
|
|
44
46
|
* Removes the Automerge document with the given ID from storage
|
|
45
47
|
*/
|
|
46
48
|
removeDoc(documentId: DocumentId): Promise<void>;
|
|
47
|
-
loadSyncState(documentId: DocumentId,
|
|
48
|
-
saveSyncState(documentId: DocumentId,
|
|
49
|
+
loadSyncState(documentId: DocumentId, storageId: StorageId): Promise<A.SyncState | undefined>;
|
|
50
|
+
saveSyncState(documentId: DocumentId, storageId: StorageId, syncState: A.SyncState): Promise<void>;
|
|
49
51
|
}
|
|
50
52
|
//# sourceMappingURL=StorageSubsystem.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageSubsystem.d.ts","sourceRoot":"","sources":["../../src/storage/StorageSubsystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAI9C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"StorageSubsystem.d.ts","sourceRoot":"","sources":["../../src/storage/StorageSubsystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAI9C,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAyB,SAAS,EAAE,MAAM,YAAY,CAAA;AAK7D;;;GAGG;AACH,qBAAa,gBAAgB;;gBAef,cAAc,EAAE,cAAc;IAIpC,EAAE,IAAI,OAAO,CAAC,SAAS,CAAC;IA2B9B,kCAAkC;IAC5B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAKlC,gCAAgC;IAC1B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM;IAEX,sCAAsC;IACtC,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,IAAI,CAAC;IAKhB,oCAAoC;IAC9B,MAAM;IACV,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,2FAA2F;IAC3F,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC;IAOhB;;OAEG;IACG,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAmClE;;;;;;OAMG;IACG,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAazE;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,UAAU;IAkEhC,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC;IAM7B,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,CAAC,CAAC,SAAS,GACrB,OAAO,CAAC,IAAI,CAAC;CA8CjB"}
|
|
@@ -4,6 +4,7 @@ import { headsAreSame } from "../helpers/headsAreSame.js";
|
|
|
4
4
|
import { mergeArrays } from "../helpers/mergeArrays.js";
|
|
5
5
|
import { keyHash, headsHash } from "./keyHash.js";
|
|
6
6
|
import { chunkTypeFromKey } from "./chunkTypeFromKey.js";
|
|
7
|
+
import * as Uuid from "uuid";
|
|
7
8
|
/**
|
|
8
9
|
* The storage subsystem is responsible for saving and loading Automerge documents to and from
|
|
9
10
|
* storage adapter. It also provides a generic key/value storage interface for other uses.
|
|
@@ -21,6 +22,18 @@ export class StorageSubsystem {
|
|
|
21
22
|
constructor(storageAdapter) {
|
|
22
23
|
this.#storageAdapter = storageAdapter;
|
|
23
24
|
}
|
|
25
|
+
async id() {
|
|
26
|
+
let storedId = await this.#storageAdapter.load(["storage-adapter-id"]);
|
|
27
|
+
let id;
|
|
28
|
+
if (storedId) {
|
|
29
|
+
id = new TextDecoder().decode(storedId);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
id = Uuid.v4();
|
|
33
|
+
await this.#storageAdapter.save(["storage-adapter-id"], new TextEncoder().encode(id));
|
|
34
|
+
}
|
|
35
|
+
return id;
|
|
36
|
+
}
|
|
24
37
|
// ARBITRARY KEY/VALUE STORAGE
|
|
25
38
|
// The `load`, `save`, and `remove` methods are for generic key/value storage, as opposed to
|
|
26
39
|
// Automerge documents. For example, they're used by the LocalFirstAuthProvider to persist the
|
|
@@ -163,13 +176,13 @@ export class StorageSubsystem {
|
|
|
163
176
|
this.#chunkInfos.set(documentId, newChunkInfos);
|
|
164
177
|
this.#compacting = false;
|
|
165
178
|
}
|
|
166
|
-
async loadSyncState(documentId,
|
|
167
|
-
const key = [documentId, "sync-state",
|
|
179
|
+
async loadSyncState(documentId, storageId) {
|
|
180
|
+
const key = [documentId, "sync-state", storageId];
|
|
168
181
|
const loaded = await this.#storageAdapter.load(key);
|
|
169
182
|
return loaded ? A.decodeSyncState(loaded) : undefined;
|
|
170
183
|
}
|
|
171
|
-
async saveSyncState(documentId,
|
|
172
|
-
const key = [documentId, "sync-state",
|
|
184
|
+
async saveSyncState(documentId, storageId, syncState) {
|
|
185
|
+
const key = [documentId, "sync-state", storageId];
|
|
173
186
|
await this.#storageAdapter.save(key, A.encodeSyncState(syncState));
|
|
174
187
|
}
|
|
175
188
|
/**
|
|
@@ -204,6 +217,11 @@ export class StorageSubsystem {
|
|
|
204
217
|
incrementalSize += chunk.size;
|
|
205
218
|
}
|
|
206
219
|
}
|
|
207
|
-
|
|
220
|
+
// if the file is currently small, don't worry, just compact
|
|
221
|
+
// this might seem a bit arbitrary (1k is arbitrary) but is designed to ensure compaction
|
|
222
|
+
// for documents with only a single large change on top of an empty (or nearly empty) document
|
|
223
|
+
// for example: imported NPM modules, images, etc.
|
|
224
|
+
// if we have even more incrementals (so far) than the snapshot, compact
|
|
225
|
+
return snapshotSize < 1024 || incrementalSize >= snapshotSize;
|
|
208
226
|
}
|
|
209
227
|
}
|
package/dist/storage/types.d.ts
CHANGED
|
@@ -34,4 +34,8 @@ export type ChunkType = "snapshot" | "incremental";
|
|
|
34
34
|
* should not assume any particular structure.
|
|
35
35
|
**/
|
|
36
36
|
export type StorageKey = string[];
|
|
37
|
+
/** A branded type for storage IDs */
|
|
38
|
+
export type StorageId = string & {
|
|
39
|
+
__storageId: true;
|
|
40
|
+
};
|
|
37
41
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,UAAU,CAAA;IACf,IAAI,EAAE,UAAU,GAAG,SAAS,CAAA;CAC7B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,UAAU,CAAA;IACf,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;AAElD;;;;;;;;;;;;;;;;;IAiBI;AACJ,MAAM,MAAM,UAAU,GAAG,MAAM,EAAE,CAAA"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,UAAU,CAAA;IACf,IAAI,EAAE,UAAU,GAAG,SAAS,CAAA;CAC7B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,UAAU,CAAA;IACf,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;AAElD;;;;;;;;;;;;;;;;;IAiBI;AACJ,MAAM,MAAM,UAAU,GAAG,MAAM,EAAE,CAAA;AAEjC,qCAAqC;AACrC,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Repo } from "../Repo.js";
|
|
2
|
-
import {
|
|
2
|
+
import { DocMessage } from "../network/messages.js";
|
|
3
3
|
import { DocumentId, PeerId } from "../types.js";
|
|
4
4
|
import { Synchronizer } from "./Synchronizer.js";
|
|
5
5
|
/** A CollectionSynchronizer is responsible for synchronizing a DocCollection with peers. */
|
|
@@ -11,7 +11,7 @@ export declare class CollectionSynchronizer extends Synchronizer {
|
|
|
11
11
|
* When we receive a sync message for a document we haven't got in memory, we
|
|
12
12
|
* register it with the repo and start synchronizing
|
|
13
13
|
*/
|
|
14
|
-
receiveMessage(message:
|
|
14
|
+
receiveMessage(message: DocMessage): Promise<void>;
|
|
15
15
|
/**
|
|
16
16
|
* Starts synchronizing the given document with all peers that we share it generously with.
|
|
17
17
|
*/
|
|
@@ -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,
|
|
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;AAChE,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;IAoD9B;;;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"}
|
|
@@ -28,11 +28,15 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
28
28
|
#initDocSynchronizer(handle) {
|
|
29
29
|
const docSynchronizer = new DocSynchronizer({
|
|
30
30
|
handle,
|
|
31
|
-
onLoadSyncState: peerId => {
|
|
31
|
+
onLoadSyncState: async (peerId) => {
|
|
32
32
|
if (!this.repo.storageSubsystem) {
|
|
33
|
-
return
|
|
33
|
+
return;
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
const { storageId, isEphemeral } = this.repo.peerMetadataByPeerId[peerId] || {};
|
|
36
|
+
if (!storageId || isEphemeral) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
return this.repo.storageSubsystem.loadSyncState(handle.documentId, storageId);
|
|
36
40
|
},
|
|
37
41
|
});
|
|
38
42
|
docSynchronizer.on("message", event => this.emit("message", event));
|
|
@@ -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;AAIhD,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;AAIhD,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;IAyHD,OAAO,CAAC,MAAM,EAAE,MAAM;IAItB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IA+C3B,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"}
|
|
@@ -5,7 +5,6 @@ import { READY, REQUESTING, UNAVAILABLE, } from "../DocHandle.js";
|
|
|
5
5
|
import { isRequestMessage, } from "../network/messages.js";
|
|
6
6
|
import { Synchronizer } from "./Synchronizer.js";
|
|
7
7
|
import { throttle } from "../helpers/throttle.js";
|
|
8
|
-
import { headsAreSame } from "../helpers/headsAreSame.js";
|
|
9
8
|
/**
|
|
10
9
|
* DocSynchronizer takes a handle to an Automerge document, and receives & dispatches sync messages
|
|
11
10
|
* to bring it inline with all other peers' versions.
|
|
@@ -98,15 +97,7 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
98
97
|
this.#syncStates[peerId] = syncState;
|
|
99
98
|
}
|
|
100
99
|
#setSyncState(peerId, syncState) {
|
|
101
|
-
const previousSyncState = this.#syncStates[peerId];
|
|
102
100
|
this.#syncStates[peerId] = syncState;
|
|
103
|
-
const haveTheirSyncedHeadsChanged = syncState.theirHeads &&
|
|
104
|
-
(!previousSyncState ||
|
|
105
|
-
!previousSyncState.theirHeads ||
|
|
106
|
-
!headsAreSame(previousSyncState.theirHeads, syncState.theirHeads));
|
|
107
|
-
if (haveTheirSyncedHeadsChanged) {
|
|
108
|
-
this.#handle.setRemoteHeads(peerId, syncState.theirHeads);
|
|
109
|
-
}
|
|
110
101
|
this.emit("sync-state", {
|
|
111
102
|
peerId,
|
|
112
103
|
syncState,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.1.0-alpha.2",
|
|
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>",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"http-server": "^14.1.0"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@automerge/automerge": "^2.1.
|
|
26
|
+
"@automerge/automerge": "^2.1.8-alpha.1",
|
|
27
27
|
"bs58check": "^3.0.1",
|
|
28
28
|
"cbor-x": "^1.3.0",
|
|
29
29
|
"debug": "^4.3.4",
|
|
@@ -55,5 +55,5 @@
|
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "d73e71588c3835a172fdf4d19e56a1f946c041ed"
|
|
59
59
|
}
|
package/src/DocHandle.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { encode } from "./helpers/cbor.js"
|
|
|
19
19
|
import { headsAreSame } from "./helpers/headsAreSame.js"
|
|
20
20
|
import { withTimeout } from "./helpers/withTimeout.js"
|
|
21
21
|
import type { AutomergeUrl, DocumentId, PeerId } from "./types.js"
|
|
22
|
+
import { StorageId } from "./storage/types.js"
|
|
22
23
|
|
|
23
24
|
/** DocHandle is a wrapper around a single Automerge document that lets us
|
|
24
25
|
* listen for changes and notify the network and storage of new changes.
|
|
@@ -39,7 +40,7 @@ export class DocHandle<T> //
|
|
|
39
40
|
|
|
40
41
|
#machine: DocHandleXstateMachine<T>
|
|
41
42
|
#timeoutDelay: number
|
|
42
|
-
#remoteHeads: Record<
|
|
43
|
+
#remoteHeads: Record<StorageId, A.Heads> = {}
|
|
43
44
|
|
|
44
45
|
/** The URL of this document
|
|
45
46
|
*
|
|
@@ -325,17 +326,17 @@ export class DocHandle<T> //
|
|
|
325
326
|
})
|
|
326
327
|
}
|
|
327
328
|
|
|
328
|
-
/** `setRemoteHeads` is called by the doc
|
|
329
|
+
/** `setRemoteHeads` is called by the repo either when a doc handle changes or we receive new remote heads
|
|
329
330
|
* @hidden
|
|
330
331
|
*/
|
|
331
|
-
setRemoteHeads(
|
|
332
|
-
this.#remoteHeads[
|
|
333
|
-
this.emit("remote-heads", {
|
|
332
|
+
setRemoteHeads(storageId: StorageId, heads: A.Heads) {
|
|
333
|
+
this.#remoteHeads[storageId] = heads
|
|
334
|
+
this.emit("remote-heads", { storageId, heads })
|
|
334
335
|
}
|
|
335
336
|
|
|
336
|
-
/** Returns the heads of the
|
|
337
|
-
getRemoteHeads(
|
|
338
|
-
return this.#remoteHeads[
|
|
337
|
+
/** Returns the heads of the storageId */
|
|
338
|
+
getRemoteHeads(storageId: StorageId): A.Heads | undefined {
|
|
339
|
+
return this.#remoteHeads[storageId]
|
|
339
340
|
}
|
|
340
341
|
|
|
341
342
|
/** `change` is called by the repo when the document is changed locally */
|
|
@@ -494,7 +495,7 @@ export interface DocHandleOutboundEphemeralMessagePayload<T> {
|
|
|
494
495
|
}
|
|
495
496
|
|
|
496
497
|
export interface DocHandleRemoteHeadsPayload {
|
|
497
|
-
|
|
498
|
+
storageId: StorageId
|
|
498
499
|
heads: A.Heads
|
|
499
500
|
}
|
|
500
501
|
|