@automerge/automerge-repo 1.0.19 → 1.1.0-alpha.13
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 +12 -7
- package/dist/AutomergeUrl.js +2 -2
- 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 +42 -0
- package/dist/RemoteHeadsSubscriptions.d.ts.map +1 -0
- package/dist/RemoteHeadsSubscriptions.js +284 -0
- package/dist/Repo.d.ts +29 -2
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +168 -9
- package/dist/helpers/debounce.js +1 -1
- package/dist/helpers/pause.d.ts.map +1 -1
- package/dist/helpers/pause.js +2 -0
- package/dist/helpers/throttle.js +1 -1
- package/dist/helpers/withTimeout.d.ts.map +1 -1
- package/dist/helpers/withTimeout.js +2 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/network/NetworkAdapter.d.ts +15 -1
- package/dist/network/NetworkAdapter.d.ts.map +1 -1
- package/dist/network/NetworkAdapter.js +3 -1
- package/dist/network/NetworkSubsystem.d.ts +4 -2
- package/dist/network/NetworkSubsystem.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.js +13 -7
- package/dist/network/messages.d.ts +68 -35
- package/dist/network/messages.d.ts.map +1 -1
- package/dist/network/messages.js +9 -7
- 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/keyHash.d.ts.map +1 -1
- 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 +9 -3
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +20 -17
- package/dist/synchronizer/Synchronizer.d.ts +12 -3
- package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/AutomergeUrl.ts +2 -2
- package/src/DocHandle.ts +10 -9
- package/src/RemoteHeadsSubscriptions.ts +375 -0
- package/src/Repo.ts +241 -16
- package/src/helpers/debounce.ts +1 -1
- package/src/helpers/pause.ts +4 -0
- package/src/helpers/throttle.ts +1 -1
- package/src/helpers/withTimeout.ts +2 -0
- package/src/index.ts +3 -1
- package/src/network/NetworkAdapter.ts +19 -2
- package/src/network/NetworkSubsystem.ts +21 -9
- package/src/network/messages.ts +88 -50
- package/src/storage/StorageSubsystem.ts +30 -7
- package/src/storage/keyHash.ts +2 -0
- package/src/storage/types.ts +3 -0
- package/src/synchronizer/CollectionSynchronizer.ts +13 -5
- package/src/synchronizer/DocSynchronizer.ts +27 -27
- package/src/synchronizer/Synchronizer.ts +13 -3
- package/test/DocHandle.test.ts +0 -17
- package/test/RemoteHeadsSubscriptions.test.ts +353 -0
- package/test/Repo.test.ts +108 -17
- package/test/StorageSubsystem.test.ts +29 -7
- package/test/helpers/waitForMessages.ts +22 -0
- package/test/remoteHeads.test.ts +260 -0
- package/.eslintrc +0 -28
|
@@ -1,55 +1,64 @@
|
|
|
1
1
|
import { SyncState } from "@automerge/automerge";
|
|
2
|
+
import { StorageId } from "../storage/types.js";
|
|
2
3
|
import { DocumentId, PeerId, SessionId } from "../types.js";
|
|
4
|
+
export type Message = {
|
|
5
|
+
type: string;
|
|
6
|
+
/** The peer ID of the sender of this message */
|
|
7
|
+
senderId: PeerId;
|
|
8
|
+
/** The peer ID of the recipient of this message */
|
|
9
|
+
targetId: PeerId;
|
|
10
|
+
data?: Uint8Array;
|
|
11
|
+
documentId?: DocumentId;
|
|
12
|
+
};
|
|
3
13
|
/**
|
|
4
14
|
* A sync message for a particular document
|
|
5
15
|
*/
|
|
6
16
|
export type SyncMessage = {
|
|
7
17
|
type: "sync";
|
|
8
|
-
/** The peer ID of the sender of this message */
|
|
9
18
|
senderId: PeerId;
|
|
10
|
-
/** The peer ID of the recipient of this message */
|
|
11
19
|
targetId: PeerId;
|
|
12
20
|
/** The automerge sync message */
|
|
13
21
|
data: Uint8Array;
|
|
14
22
|
/** The document ID of the document this message is for */
|
|
15
23
|
documentId: DocumentId;
|
|
16
24
|
};
|
|
17
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* An ephemeral message.
|
|
18
27
|
*
|
|
19
28
|
* @remarks
|
|
20
|
-
* Ephemeral messages are not persisted anywhere
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* number
|
|
25
|
-
* we have already seen.
|
|
29
|
+
* Ephemeral messages are not persisted anywhere. The data property can be used by the application
|
|
30
|
+
* as needed. The repo gossips these around.
|
|
31
|
+
*
|
|
32
|
+
* In order to avoid infinite loops of ephemeral messages, every message has (a) a session ID, which
|
|
33
|
+
* is a random number generated by the sender at startup time; and (b) a sequence number. The
|
|
34
|
+
* combination of these two things allows us to discard messages we have already seen.
|
|
26
35
|
* */
|
|
27
36
|
export type EphemeralMessage = {
|
|
28
37
|
type: "ephemeral";
|
|
29
|
-
/** The peer ID of the sender of this message */
|
|
30
38
|
senderId: PeerId;
|
|
31
|
-
/** The peer ID of the recipient of this message */
|
|
32
39
|
targetId: PeerId;
|
|
33
|
-
/** A sequence number which must be incremented for each message sent by this peer */
|
|
40
|
+
/** A sequence number which must be incremented for each message sent by this peer. */
|
|
34
41
|
count: number;
|
|
35
|
-
/** The ID of the session this message is part of. The sequence number for a given session always increases */
|
|
42
|
+
/** The ID of the session this message is part of. The sequence number for a given session always increases. */
|
|
36
43
|
sessionId: SessionId;
|
|
37
|
-
/** The document ID this message pertains to */
|
|
44
|
+
/** The document ID this message pertains to. */
|
|
38
45
|
documentId: DocumentId;
|
|
39
|
-
/** The actual data of the message */
|
|
46
|
+
/** The actual data of the message. */
|
|
40
47
|
data: Uint8Array;
|
|
41
48
|
};
|
|
42
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Sent by a {@link Repo} to indicate that it does not have the document and none of its connected
|
|
51
|
+
* peers do either.
|
|
52
|
+
*/
|
|
43
53
|
export type DocumentUnavailableMessage = {
|
|
44
54
|
type: "doc-unavailable";
|
|
45
|
-
/** The peer ID of the sender of this message */
|
|
46
55
|
senderId: PeerId;
|
|
47
|
-
/** The peer ID of the recipient of this message */
|
|
48
56
|
targetId: PeerId;
|
|
49
57
|
/** The document which the peer claims it doesn't have */
|
|
50
58
|
documentId: DocumentId;
|
|
51
59
|
};
|
|
52
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Sent by a {@link Repo} to request a document from a peer.
|
|
53
62
|
*
|
|
54
63
|
* @remarks
|
|
55
64
|
* This is identical to a {@link SyncMessage} except that it is sent by a {@link Repo}
|
|
@@ -57,42 +66,66 @@ export type DocumentUnavailableMessage = {
|
|
|
57
66
|
* */
|
|
58
67
|
export type RequestMessage = {
|
|
59
68
|
type: "request";
|
|
60
|
-
/** The peer ID of the sender of this message */
|
|
61
69
|
senderId: PeerId;
|
|
62
|
-
/** The peer ID of the recipient of this message */
|
|
63
70
|
targetId: PeerId;
|
|
64
|
-
/** The
|
|
71
|
+
/** The automerge sync message */
|
|
65
72
|
data: Uint8Array;
|
|
66
|
-
/** The document ID this message
|
|
73
|
+
/** The document ID of the document this message is for */
|
|
67
74
|
documentId: DocumentId;
|
|
68
75
|
};
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Sent by a {@link Repo} to add or remove storage IDs from a remote peer's subscription.
|
|
78
|
+
*/
|
|
79
|
+
export type RemoteSubscriptionControlMessage = {
|
|
80
|
+
type: "remote-subscription-change";
|
|
81
|
+
senderId: PeerId;
|
|
82
|
+
targetId: PeerId;
|
|
83
|
+
/** The storage IDs to add to the subscription */
|
|
84
|
+
add?: StorageId[];
|
|
85
|
+
/** The storage IDs to remove from the subscription */
|
|
86
|
+
remove?: StorageId[];
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Sent by a {@link Repo} to indicate that the heads of a document have changed on a remote peer.
|
|
90
|
+
*/
|
|
91
|
+
export type RemoteHeadsChanged = {
|
|
92
|
+
type: "remote-heads-changed";
|
|
73
93
|
senderId: PeerId;
|
|
74
|
-
/** The peer ID of the recipient of this message */
|
|
75
94
|
targetId: PeerId;
|
|
76
|
-
/** The
|
|
77
|
-
|
|
95
|
+
/** The document ID of the document that has changed */
|
|
96
|
+
documentId: DocumentId;
|
|
97
|
+
/** The document's new heads */
|
|
98
|
+
newHeads: {
|
|
99
|
+
[key: StorageId]: {
|
|
100
|
+
heads: string[];
|
|
101
|
+
timestamp: number;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
78
104
|
};
|
|
79
105
|
/** These are message types that a {@link NetworkAdapter} surfaces to a {@link Repo}. */
|
|
80
|
-
export type RepoMessage = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage;
|
|
81
|
-
/** These are
|
|
82
|
-
export type
|
|
106
|
+
export type RepoMessage = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage | RemoteSubscriptionControlMessage | RemoteHeadsChanged;
|
|
107
|
+
/** These are message types that are handled by the {@link CollectionSynchronizer}.*/
|
|
108
|
+
export type DocMessage = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage;
|
|
83
109
|
/**
|
|
84
110
|
* The contents of a message, without the sender ID or other properties added by the {@link NetworkSubsystem})
|
|
85
111
|
*/
|
|
86
|
-
export type MessageContents<T extends Message =
|
|
112
|
+
export type MessageContents<T extends Message = RepoMessage> = T extends EphemeralMessage ? Omit<T, "senderId" | "count" | "sessionId"> : Omit<T, "senderId">;
|
|
87
113
|
/** Notify the repo that the sync state has changed */
|
|
88
114
|
export interface SyncStateMessage {
|
|
89
115
|
peerId: PeerId;
|
|
90
116
|
documentId: DocumentId;
|
|
91
117
|
syncState: SyncState;
|
|
92
118
|
}
|
|
93
|
-
|
|
119
|
+
/** Notify the repo that a peer started syncing with a doc */
|
|
120
|
+
export interface OpenDocMessage {
|
|
121
|
+
peerId: PeerId;
|
|
122
|
+
documentId: DocumentId;
|
|
123
|
+
}
|
|
124
|
+
export declare const isRepoMessage: (message: Message) => message is RepoMessage;
|
|
94
125
|
export declare const isDocumentUnavailableMessage: (msg: Message) => msg is DocumentUnavailableMessage;
|
|
95
126
|
export declare const isRequestMessage: (msg: Message) => msg is RequestMessage;
|
|
96
127
|
export declare const isSyncMessage: (msg: Message) => msg is SyncMessage;
|
|
97
128
|
export declare const isEphemeralMessage: (msg: Message) => msg is EphemeralMessage;
|
|
129
|
+
export declare const isRemoteSubscriptionControlMessage: (msg: Message) => msg is RemoteSubscriptionControlMessage;
|
|
130
|
+
export declare const isRemoteHeadsChanged: (msg: Message) => msg is RemoteHeadsChanged;
|
|
98
131
|
//# 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;AAE3D
|
|
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
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
// TYPE GUARDS
|
|
2
|
-
export const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
isDocumentUnavailableMessage(message));
|
|
2
|
+
export const isRepoMessage = (message) => isSyncMessage(message) ||
|
|
3
|
+
isEphemeralMessage(message) ||
|
|
4
|
+
isRequestMessage(message) ||
|
|
5
|
+
isDocumentUnavailableMessage(message) ||
|
|
6
|
+
isRemoteSubscriptionControlMessage(message) ||
|
|
7
|
+
isRemoteHeadsChanged(message);
|
|
9
8
|
// prettier-ignore
|
|
10
9
|
export const isDocumentUnavailableMessage = (msg) => msg.type === "doc-unavailable";
|
|
11
10
|
export const isRequestMessage = (msg) => msg.type === "request";
|
|
12
11
|
export const isSyncMessage = (msg) => msg.type === "sync";
|
|
13
12
|
export const isEphemeralMessage = (msg) => msg.type === "ephemeral";
|
|
13
|
+
// prettier-ignore
|
|
14
|
+
export const isRemoteSubscriptionControlMessage = (msg) => msg.type === "remote-subscription-change";
|
|
15
|
+
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
|
+
const 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
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyHash.d.ts","sourceRoot":"","sources":["../../src/storage/keyHash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAI9C,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,UAIzC;
|
|
1
|
+
{"version":3,"file":"keyHash.d.ts","sourceRoot":"","sources":["../../src/storage/keyHash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAI9C,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,UAIzC;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,GAAG,MAAM,CAIhD"}
|
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;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;IAalC,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,14 +28,19 @@ 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));
|
|
43
|
+
docSynchronizer.on("open-doc", event => this.emit("open-doc", event));
|
|
39
44
|
docSynchronizer.on("sync-state", event => this.emit("sync-state", event));
|
|
40
45
|
return docSynchronizer;
|
|
41
46
|
}
|
|
@@ -82,6 +87,7 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
82
87
|
});
|
|
83
88
|
}
|
|
84
89
|
// TODO: implement this
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
85
91
|
removeDocument(documentId) {
|
|
86
92
|
throw new Error("not implemented");
|
|
87
93
|
}
|
|
@@ -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;
|
|
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;IAkID,OAAO,CAAC,MAAM,EAAE,MAAM;IAItB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IAmD3B,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.
|
|
@@ -67,9 +66,7 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
67
66
|
this.emit("message", message);
|
|
68
67
|
}
|
|
69
68
|
#withSyncState(peerId, callback) {
|
|
70
|
-
|
|
71
|
-
this.#peers.push(peerId);
|
|
72
|
-
}
|
|
69
|
+
this.#addPeer(peerId);
|
|
73
70
|
if (!(peerId in this.#peerDocumentStatuses)) {
|
|
74
71
|
this.#peerDocumentStatuses[peerId] = "unknown";
|
|
75
72
|
}
|
|
@@ -80,13 +77,23 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
80
77
|
}
|
|
81
78
|
let pendingCallbacks = this.#pendingSyncStateCallbacks[peerId];
|
|
82
79
|
if (!pendingCallbacks) {
|
|
83
|
-
this.#onLoadSyncState(peerId)
|
|
80
|
+
this.#onLoadSyncState(peerId)
|
|
81
|
+
.then(syncState => {
|
|
84
82
|
this.#initSyncState(peerId, syncState ?? A.initSyncState());
|
|
83
|
+
})
|
|
84
|
+
.catch(err => {
|
|
85
|
+
this.#log(`Error loading sync state for ${peerId}: ${err}`);
|
|
85
86
|
});
|
|
86
87
|
pendingCallbacks = this.#pendingSyncStateCallbacks[peerId] = [];
|
|
87
88
|
}
|
|
88
89
|
pendingCallbacks.push(callback);
|
|
89
90
|
}
|
|
91
|
+
#addPeer(peerId) {
|
|
92
|
+
if (!this.#peers.includes(peerId)) {
|
|
93
|
+
this.#peers.push(peerId);
|
|
94
|
+
this.emit("open-doc", { documentId: this.documentId, peerId });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
90
97
|
#initSyncState(peerId, syncState) {
|
|
91
98
|
const pendingCallbacks = this.#pendingSyncStateCallbacks[peerId];
|
|
92
99
|
if (pendingCallbacks) {
|
|
@@ -98,15 +105,7 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
98
105
|
this.#syncStates[peerId] = syncState;
|
|
99
106
|
}
|
|
100
107
|
#setSyncState(peerId, syncState) {
|
|
101
|
-
const previousSyncState = this.#syncStates[peerId];
|
|
102
108
|
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
109
|
this.emit("sync-state", {
|
|
111
110
|
peerId,
|
|
112
111
|
syncState,
|
|
@@ -179,10 +178,14 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
179
178
|
// TODO: cover that case with a test and remove this hack
|
|
180
179
|
const reparsedSyncState = A.decodeSyncState(A.encodeSyncState(syncState));
|
|
181
180
|
this.#setSyncState(peerId, reparsedSyncState);
|
|
182
|
-
docPromise
|
|
181
|
+
docPromise
|
|
182
|
+
.then(doc => {
|
|
183
183
|
if (doc) {
|
|
184
184
|
this.#sendSyncMessage(peerId, doc);
|
|
185
185
|
}
|
|
186
|
+
})
|
|
187
|
+
.catch(err => {
|
|
188
|
+
this.#log(`Error loading doc for ${peerId}: ${err}`);
|
|
186
189
|
});
|
|
187
190
|
});
|
|
188
191
|
});
|
|
@@ -236,9 +239,9 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
236
239
|
return;
|
|
237
240
|
}
|
|
238
241
|
this.#processAllPendingSyncMessages();
|
|
239
|
-
this.#processSyncMessage(message
|
|
242
|
+
this.#processSyncMessage(message);
|
|
240
243
|
}
|
|
241
|
-
#processSyncMessage(message
|
|
244
|
+
#processSyncMessage(message) {
|
|
242
245
|
if (isRequestMessage(message)) {
|
|
243
246
|
this.#peerDocumentStatuses[message.senderId] = "wants";
|
|
244
247
|
}
|
|
@@ -279,7 +282,7 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
279
282
|
}
|
|
280
283
|
#processAllPendingSyncMessages() {
|
|
281
284
|
for (const message of this.#pendingSyncMessages) {
|
|
282
|
-
this.#processSyncMessage(message.message
|
|
285
|
+
this.#processSyncMessage(message.message);
|
|
283
286
|
}
|
|
284
287
|
this.#pendingSyncMessages = [];
|
|
285
288
|
}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { EventEmitter } from "eventemitter3";
|
|
2
|
-
import { MessageContents,
|
|
2
|
+
import { MessageContents, OpenDocMessage, RepoMessage } from "../network/messages.js";
|
|
3
|
+
import { SyncState } from "@automerge/automerge";
|
|
4
|
+
import { PeerId, DocumentId } from "../types.js";
|
|
3
5
|
export declare abstract class Synchronizer extends EventEmitter<SynchronizerEvents> {
|
|
4
6
|
abstract receiveMessage(message: RepoMessage): void;
|
|
5
7
|
}
|
|
6
8
|
export interface SynchronizerEvents {
|
|
7
|
-
message: (
|
|
8
|
-
"sync-state": (
|
|
9
|
+
message: (payload: MessageContents) => void;
|
|
10
|
+
"sync-state": (payload: SyncStatePayload) => void;
|
|
11
|
+
"open-doc": (arg: OpenDocMessage) => void;
|
|
12
|
+
}
|
|
13
|
+
/** Notify the repo that the sync state has changed */
|
|
14
|
+
export interface SyncStatePayload {
|
|
15
|
+
peerId: PeerId;
|
|
16
|
+
documentId: DocumentId;
|
|
17
|
+
syncState: SyncState;
|
|
9
18
|
}
|
|
10
19
|
//# sourceMappingURL=Synchronizer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Synchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/Synchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EACL,eAAe,EACf,
|
|
1
|
+
{"version":3,"file":"Synchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/Synchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EACL,eAAe,EACf,cAAc,EACd,WAAW,EACZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEhD,8BAAsB,YAAa,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IACzE,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;CACpD;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAA;IAC3C,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAA;IACjD,UAAU,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CAC1C;AAED,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;CACrB"}
|
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.13",
|
|
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>",
|
|
@@ -10,20 +10,20 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc",
|
|
12
12
|
"watch": "npm-watch build",
|
|
13
|
-
"test:coverage": "c8 --reporter=lcov --reporter=html --reporter=text
|
|
13
|
+
"test:coverage": "c8 --reporter=lcov --reporter=html --reporter=text pnpm test",
|
|
14
14
|
"test": "vitest",
|
|
15
15
|
"test:watch": "npm-watch test",
|
|
16
|
-
"test:log": "cross-env DEBUG='automerge-repo:*' yarn test",
|
|
17
16
|
"fuzz": "ts-node --esm --experimentalSpecifierResolution=node fuzz/fuzz.ts"
|
|
18
17
|
},
|
|
19
18
|
"browser": {
|
|
20
19
|
"crypto": false
|
|
21
20
|
},
|
|
22
21
|
"devDependencies": {
|
|
23
|
-
"http-server": "^14.1.0"
|
|
22
|
+
"http-server": "^14.1.0",
|
|
23
|
+
"vite": "^5.0.8"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@automerge/automerge": "^2.1.
|
|
26
|
+
"@automerge/automerge": "^2.1.9",
|
|
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": "f4ce1376d900ad98f00a638626be9611077460b5"
|
|
59
59
|
}
|
package/src/AutomergeUrl.ts
CHANGED
|
@@ -13,7 +13,7 @@ export const urlPrefix = "automerge:"
|
|
|
13
13
|
/** Given an Automerge URL, returns the DocumentId in both base58check-encoded form and binary form */
|
|
14
14
|
export const parseAutomergeUrl = (url: AutomergeUrl) => {
|
|
15
15
|
const regex = new RegExp(`^${urlPrefix}(\\w+)$`)
|
|
16
|
-
const [
|
|
16
|
+
const [, docMatch] = url.match(regex) || []
|
|
17
17
|
const documentId = docMatch as DocumentId
|
|
18
18
|
const binaryDocumentId = documentIdToBinary(documentId)
|
|
19
19
|
|
|
@@ -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
|
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
|
|