@automerge/automerge-repo 1.1.0-alpha.6 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -7
- package/dist/AutomergeUrl.js +2 -2
- package/dist/DocHandle.d.ts +10 -4
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +17 -8
- package/dist/RemoteHeadsSubscriptions.js +3 -3
- package/dist/Repo.d.ts +23 -6
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +104 -71
- package/dist/helpers/debounce.js +1 -1
- package/dist/helpers/pause.d.ts +0 -1
- package/dist/helpers/pause.d.ts.map +1 -1
- package/dist/helpers/pause.js +2 -8
- 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 +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/network/NetworkAdapter.d.ts.map +1 -1
- package/dist/network/NetworkAdapter.js +2 -1
- package/dist/network/NetworkSubsystem.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.js +5 -3
- package/dist/network/messages.d.ts +43 -38
- package/dist/network/messages.d.ts.map +1 -1
- package/dist/network/messages.js +7 -9
- package/dist/storage/StorageSubsystem.js +1 -1
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +1 -0
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +13 -5
- package/dist/synchronizer/Synchronizer.d.ts +11 -3
- package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/AutomergeUrl.ts +2 -2
- package/src/DocHandle.ts +34 -12
- package/src/RemoteHeadsSubscriptions.ts +3 -3
- package/src/Repo.ts +130 -81
- package/src/helpers/debounce.ts +1 -1
- package/src/helpers/pause.ts +3 -11
- package/src/helpers/throttle.ts +1 -1
- package/src/helpers/withTimeout.ts +2 -0
- package/src/index.ts +1 -1
- package/src/network/NetworkAdapter.ts +5 -3
- package/src/network/NetworkSubsystem.ts +5 -4
- package/src/network/messages.ts +60 -63
- package/src/storage/StorageSubsystem.ts +1 -1
- package/src/synchronizer/CollectionSynchronizer.ts +2 -1
- package/src/synchronizer/DocSynchronizer.ts +19 -11
- package/src/synchronizer/Synchronizer.ts +11 -3
- package/test/CollectionSynchronizer.test.ts +7 -5
- package/test/DocHandle.test.ts +11 -2
- package/test/RemoteHeadsSubscriptions.test.ts +53 -50
- package/test/Repo.test.ts +64 -2
- package/test/StorageSubsystem.test.ts +1 -1
- package/test/helpers/collectMessages.ts +19 -0
- package/test/remoteHeads.test.ts +141 -120
- package/.eslintrc +0 -28
- package/test/helpers/waitForMessages.ts +0 -22
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ export { DocHandle } from "./DocHandle.js";
|
|
|
29
29
|
export { isValidAutomergeUrl, parseAutomergeUrl, stringifyAutomergeUrl, } from "./AutomergeUrl.js";
|
|
30
30
|
export { Repo } from "./Repo.js";
|
|
31
31
|
export { NetworkAdapter } from "./network/NetworkAdapter.js";
|
|
32
|
-
export {
|
|
32
|
+
export { isRepoMessage } from "./network/messages.js";
|
|
33
33
|
export { StorageAdapter } from "./storage/StorageAdapter.js";
|
|
34
34
|
/** @hidden **/
|
|
35
35
|
export * as cbor from "./helpers/cbor.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkAdapter.d.ts","sourceRoot":"","sources":["../../src/network/NetworkAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NetworkAdapter.d.ts","sourceRoot":"","sources":["../../src/network/NetworkAdapter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAE/C;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED;;;;;;GAMG;AACH,8BAAsB,cAAe,SAAQ,YAAY,CAAC,oBAAoB,CAAC;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,YAAY,CAAA;IAE3B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI;IAEnE;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAErC,gEAAgE;IAChE,QAAQ,CAAC,UAAU,IAAI,IAAI;CAC5B;AAID,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;IAErC,yCAAyC;IACzC,KAAK,EAAE,MAAM,IAAI,CAAA;IAEjB,+DAA+D;IAC/D,gBAAgB,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IAEzD,2EAA2E;IAC3E,mBAAmB,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAE/D,sEAAsE;IACtE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CACpC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,cAAc,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,YAAY,CAAA;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAA;CACf"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* c8 ignore start */
|
|
1
2
|
import { EventEmitter } from "eventemitter3";
|
|
2
3
|
/** An interface representing some way to connect to other peers
|
|
3
4
|
*
|
|
@@ -7,6 +8,6 @@ import { EventEmitter } from "eventemitter3";
|
|
|
7
8
|
* until the adapter emits a `ready` event before it starts trying to use it
|
|
8
9
|
*/
|
|
9
10
|
export class NetworkAdapter extends EventEmitter {
|
|
10
|
-
peerId;
|
|
11
|
+
peerId;
|
|
11
12
|
peerMetadata;
|
|
12
13
|
}
|
|
@@ -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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import debug from "debug";
|
|
2
2
|
import { EventEmitter } from "eventemitter3";
|
|
3
|
-
import { isEphemeralMessage,
|
|
3
|
+
import { isEphemeralMessage, isRepoMessage, } from "./messages.js";
|
|
4
4
|
const getEphemeralMessageSource = (message) => `${message.senderId}:${message.sessionId}`;
|
|
5
5
|
export class NetworkSubsystem extends EventEmitter {
|
|
6
6
|
peerId;
|
|
@@ -43,7 +43,7 @@ export class NetworkSubsystem extends EventEmitter {
|
|
|
43
43
|
this.emit("peer-disconnected", { peerId });
|
|
44
44
|
});
|
|
45
45
|
networkAdapter.on("message", msg => {
|
|
46
|
-
if (!
|
|
46
|
+
if (!isRepoMessage(msg)) {
|
|
47
47
|
this.#log(`invalid message: ${JSON.stringify(msg)}`);
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
@@ -69,6 +69,8 @@ export class NetworkSubsystem extends EventEmitter {
|
|
|
69
69
|
});
|
|
70
70
|
this.peerMetadata.then(peerMetadata => {
|
|
71
71
|
networkAdapter.connect(this.peerId, peerMetadata);
|
|
72
|
+
}).catch(err => {
|
|
73
|
+
this.#log("error connecting to network", err);
|
|
72
74
|
});
|
|
73
75
|
}
|
|
74
76
|
send(message) {
|
|
@@ -105,7 +107,7 @@ export class NetworkSubsystem extends EventEmitter {
|
|
|
105
107
|
}
|
|
106
108
|
};
|
|
107
109
|
const outbound = prepareMessage(message);
|
|
108
|
-
this.#log("sending message", outbound);
|
|
110
|
+
this.#log("sending message %o", outbound);
|
|
109
111
|
peer.send(outbound);
|
|
110
112
|
}
|
|
111
113
|
isReady = () => {
|
|
@@ -1,56 +1,64 @@
|
|
|
1
1
|
import { SyncState } from "@automerge/automerge";
|
|
2
|
-
import { DocumentId, PeerId, SessionId } from "../types.js";
|
|
3
2
|
import { StorageId } from "../storage/types.js";
|
|
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
|
+
};
|
|
4
13
|
/**
|
|
5
14
|
* A sync message for a particular document
|
|
6
15
|
*/
|
|
7
16
|
export type SyncMessage = {
|
|
8
17
|
type: "sync";
|
|
9
|
-
/** The peer ID of the sender of this message */
|
|
10
18
|
senderId: PeerId;
|
|
11
|
-
/** The peer ID of the recipient of this message */
|
|
12
19
|
targetId: PeerId;
|
|
13
20
|
/** The automerge sync message */
|
|
14
21
|
data: Uint8Array;
|
|
15
22
|
/** The document ID of the document this message is for */
|
|
16
23
|
documentId: DocumentId;
|
|
17
24
|
};
|
|
18
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* An ephemeral message.
|
|
19
27
|
*
|
|
20
28
|
* @remarks
|
|
21
|
-
* Ephemeral messages are not persisted anywhere
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* number
|
|
26
|
-
* 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.
|
|
27
35
|
* */
|
|
28
36
|
export type EphemeralMessage = {
|
|
29
37
|
type: "ephemeral";
|
|
30
|
-
/** The peer ID of the sender of this message */
|
|
31
38
|
senderId: PeerId;
|
|
32
|
-
/** The peer ID of the recipient of this message */
|
|
33
39
|
targetId: PeerId;
|
|
34
|
-
/** 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. */
|
|
35
41
|
count: number;
|
|
36
|
-
/** 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. */
|
|
37
43
|
sessionId: SessionId;
|
|
38
|
-
/** The document ID this message pertains to */
|
|
44
|
+
/** The document ID this message pertains to. */
|
|
39
45
|
documentId: DocumentId;
|
|
40
|
-
/** The actual data of the message */
|
|
46
|
+
/** The actual data of the message. */
|
|
41
47
|
data: Uint8Array;
|
|
42
48
|
};
|
|
43
|
-
/**
|
|
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
|
+
*/
|
|
44
53
|
export type DocumentUnavailableMessage = {
|
|
45
54
|
type: "doc-unavailable";
|
|
46
|
-
/** The peer ID of the sender of this message */
|
|
47
55
|
senderId: PeerId;
|
|
48
|
-
/** The peer ID of the recipient of this message */
|
|
49
56
|
targetId: PeerId;
|
|
50
57
|
/** The document which the peer claims it doesn't have */
|
|
51
58
|
documentId: DocumentId;
|
|
52
59
|
};
|
|
53
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Sent by a {@link Repo} to request a document from a peer.
|
|
54
62
|
*
|
|
55
63
|
* @remarks
|
|
56
64
|
* This is identical to a {@link SyncMessage} except that it is sent by a {@link Repo}
|
|
@@ -58,37 +66,35 @@ export type DocumentUnavailableMessage = {
|
|
|
58
66
|
* */
|
|
59
67
|
export type RequestMessage = {
|
|
60
68
|
type: "request";
|
|
61
|
-
/** The peer ID of the sender of this message */
|
|
62
69
|
senderId: PeerId;
|
|
63
|
-
/** The peer ID of the recipient of this message */
|
|
64
70
|
targetId: PeerId;
|
|
65
|
-
/** The
|
|
71
|
+
/** The automerge sync message */
|
|
66
72
|
data: Uint8Array;
|
|
67
|
-
/** The document ID this message
|
|
73
|
+
/** The document ID of the document this message is for */
|
|
68
74
|
documentId: DocumentId;
|
|
69
75
|
};
|
|
70
|
-
/**
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/** The peer ID of the sender of this message */
|
|
74
|
-
senderId: PeerId;
|
|
75
|
-
/** The peer ID of the recipient of this message */
|
|
76
|
-
targetId: PeerId;
|
|
77
|
-
/** The payload of the auth message (up to the specific auth provider) */
|
|
78
|
-
payload: TPayload;
|
|
79
|
-
};
|
|
76
|
+
/**
|
|
77
|
+
* Sent by a {@link Repo} to add or remove storage IDs from a remote peer's subscription.
|
|
78
|
+
*/
|
|
80
79
|
export type RemoteSubscriptionControlMessage = {
|
|
81
80
|
type: "remote-subscription-change";
|
|
82
81
|
senderId: PeerId;
|
|
83
82
|
targetId: PeerId;
|
|
83
|
+
/** The storage IDs to add to the subscription */
|
|
84
84
|
add?: StorageId[];
|
|
85
|
+
/** The storage IDs to remove from the subscription */
|
|
85
86
|
remove?: StorageId[];
|
|
86
87
|
};
|
|
88
|
+
/**
|
|
89
|
+
* Sent by a {@link Repo} to indicate that the heads of a document have changed on a remote peer.
|
|
90
|
+
*/
|
|
87
91
|
export type RemoteHeadsChanged = {
|
|
88
92
|
type: "remote-heads-changed";
|
|
89
93
|
senderId: PeerId;
|
|
90
94
|
targetId: PeerId;
|
|
95
|
+
/** The document ID of the document that has changed */
|
|
91
96
|
documentId: DocumentId;
|
|
97
|
+
/** The document's new heads */
|
|
92
98
|
newHeads: {
|
|
93
99
|
[key: StorageId]: {
|
|
94
100
|
heads: string[];
|
|
@@ -98,13 +104,12 @@ export type RemoteHeadsChanged = {
|
|
|
98
104
|
};
|
|
99
105
|
/** These are message types that a {@link NetworkAdapter} surfaces to a {@link Repo}. */
|
|
100
106
|
export type RepoMessage = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage | RemoteSubscriptionControlMessage | RemoteHeadsChanged;
|
|
107
|
+
/** These are message types that are handled by the {@link CollectionSynchronizer}.*/
|
|
101
108
|
export type DocMessage = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage;
|
|
102
|
-
/** These are all the message types that a {@link NetworkAdapter} might see. */
|
|
103
|
-
export type Message = RepoMessage | AuthMessage;
|
|
104
109
|
/**
|
|
105
110
|
* The contents of a message, without the sender ID or other properties added by the {@link NetworkSubsystem})
|
|
106
111
|
*/
|
|
107
|
-
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">;
|
|
108
113
|
/** Notify the repo that the sync state has changed */
|
|
109
114
|
export interface SyncStateMessage {
|
|
110
115
|
peerId: PeerId;
|
|
@@ -116,7 +121,7 @@ export interface OpenDocMessage {
|
|
|
116
121
|
peerId: PeerId;
|
|
117
122
|
documentId: DocumentId;
|
|
118
123
|
}
|
|
119
|
-
export declare const
|
|
124
|
+
export declare const isRepoMessage: (message: Message) => message is RepoMessage;
|
|
120
125
|
export declare const isDocumentUnavailableMessage: (msg: Message) => msg is DocumentUnavailableMessage;
|
|
121
126
|
export declare const isRequestMessage: (msg: Message) => msg is RequestMessage;
|
|
122
127
|
export declare const isSyncMessage: (msg: Message) => msg is SyncMessage;
|
|
@@ -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
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
// TYPE GUARDS
|
|
2
|
-
export const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
isDocumentUnavailableMessage(message) ||
|
|
9
|
-
isRemoteSubscriptionControlMessage(message) ||
|
|
10
|
-
isRemoteHeadsChanged(message));
|
|
2
|
+
export const isRepoMessage = (message) => isSyncMessage(message) ||
|
|
3
|
+
isEphemeralMessage(message) ||
|
|
4
|
+
isRequestMessage(message) ||
|
|
5
|
+
isDocumentUnavailableMessage(message) ||
|
|
6
|
+
isRemoteSubscriptionControlMessage(message) ||
|
|
7
|
+
isRemoteHeadsChanged(message);
|
|
11
8
|
// prettier-ignore
|
|
12
9
|
export const isDocumentUnavailableMessage = (msg) => msg.type === "doc-unavailable";
|
|
13
10
|
export const isRequestMessage = (msg) => msg.type === "request";
|
|
14
11
|
export const isSyncMessage = (msg) => msg.type === "sync";
|
|
15
12
|
export const isEphemeralMessage = (msg) => msg.type === "ephemeral";
|
|
13
|
+
// prettier-ignore
|
|
16
14
|
export const isRemoteSubscriptionControlMessage = (msg) => msg.type === "remote-subscription-change";
|
|
17
15
|
export const isRemoteHeadsChanged = (msg) => msg.type === "remote-heads-changed";
|
|
@@ -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;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"}
|
|
@@ -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;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"}
|
|
@@ -77,8 +77,12 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
77
77
|
}
|
|
78
78
|
let pendingCallbacks = this.#pendingSyncStateCallbacks[peerId];
|
|
79
79
|
if (!pendingCallbacks) {
|
|
80
|
-
this.#onLoadSyncState(peerId)
|
|
80
|
+
this.#onLoadSyncState(peerId)
|
|
81
|
+
.then(syncState => {
|
|
81
82
|
this.#initSyncState(peerId, syncState ?? A.initSyncState());
|
|
83
|
+
})
|
|
84
|
+
.catch(err => {
|
|
85
|
+
this.#log(`Error loading sync state for ${peerId}: ${err}`);
|
|
82
86
|
});
|
|
83
87
|
pendingCallbacks = this.#pendingSyncStateCallbacks[peerId] = [];
|
|
84
88
|
}
|
|
@@ -174,10 +178,14 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
174
178
|
// TODO: cover that case with a test and remove this hack
|
|
175
179
|
const reparsedSyncState = A.decodeSyncState(A.encodeSyncState(syncState));
|
|
176
180
|
this.#setSyncState(peerId, reparsedSyncState);
|
|
177
|
-
docPromise
|
|
181
|
+
docPromise
|
|
182
|
+
.then(doc => {
|
|
178
183
|
if (doc) {
|
|
179
184
|
this.#sendSyncMessage(peerId, doc);
|
|
180
185
|
}
|
|
186
|
+
})
|
|
187
|
+
.catch(err => {
|
|
188
|
+
this.#log(`Error loading doc for ${peerId}: ${err}`);
|
|
181
189
|
});
|
|
182
190
|
});
|
|
183
191
|
});
|
|
@@ -231,9 +239,9 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
231
239
|
return;
|
|
232
240
|
}
|
|
233
241
|
this.#processAllPendingSyncMessages();
|
|
234
|
-
this.#processSyncMessage(message
|
|
242
|
+
this.#processSyncMessage(message);
|
|
235
243
|
}
|
|
236
|
-
#processSyncMessage(message
|
|
244
|
+
#processSyncMessage(message) {
|
|
237
245
|
if (isRequestMessage(message)) {
|
|
238
246
|
this.#peerDocumentStatuses[message.senderId] = "wants";
|
|
239
247
|
}
|
|
@@ -274,7 +282,7 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
274
282
|
}
|
|
275
283
|
#processAllPendingSyncMessages() {
|
|
276
284
|
for (const message of this.#pendingSyncMessages) {
|
|
277
|
-
this.#processSyncMessage(message.message
|
|
285
|
+
this.#processSyncMessage(message.message);
|
|
278
286
|
}
|
|
279
287
|
this.#pendingSyncMessages = [];
|
|
280
288
|
}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { EventEmitter } from "eventemitter3";
|
|
2
|
-
import { MessageContents, OpenDocMessage, RepoMessage
|
|
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;
|
|
9
11
|
"open-doc": (arg: OpenDocMessage) => void;
|
|
10
12
|
}
|
|
13
|
+
/** Notify the repo that the sync state has changed */
|
|
14
|
+
export interface SyncStatePayload {
|
|
15
|
+
peerId: PeerId;
|
|
16
|
+
documentId: DocumentId;
|
|
17
|
+
syncState: SyncState;
|
|
18
|
+
}
|
|
11
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,cAAc,EACd,WAAW,
|
|
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.1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A repository object to manage a collection of automerge documents",
|
|
5
5
|
"repository": "https://github.com/automerge/automerge-repo/tree/master/packages/automerge-repo",
|
|
6
6
|
"author": "Peter van Hardenberg <pvh@pvh.ca>",
|
|
@@ -10,7 +10,7 @@
|
|
|
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
16
|
"fuzz": "ts-node --esm --experimentalSpecifierResolution=node fuzz/fuzz.ts"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"http-server": "^14.1.0",
|
|
23
|
-
"vite": "^
|
|
23
|
+
"vite": "^5.0.8"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@automerge/automerge": "^2.1.9",
|
|
@@ -55,5 +55,5 @@
|
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "e9e7d3f27ec2ac8a2e9d122ece80598918940067"
|
|
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
|
@@ -39,7 +39,7 @@ export class DocHandle<T> //
|
|
|
39
39
|
#log: debug.Debugger
|
|
40
40
|
|
|
41
41
|
#machine: DocHandleXstateMachine<T>
|
|
42
|
-
#timeoutDelay: number
|
|
42
|
+
#timeoutDelay: number = 60_000
|
|
43
43
|
#remoteHeads: Record<StorageId, A.Heads> = {}
|
|
44
44
|
|
|
45
45
|
/** The URL of this document
|
|
@@ -54,20 +54,30 @@ export class DocHandle<T> //
|
|
|
54
54
|
/** @hidden */
|
|
55
55
|
constructor(
|
|
56
56
|
public documentId: DocumentId,
|
|
57
|
-
|
|
57
|
+
options: DocHandleOptions<T> = {}
|
|
58
58
|
) {
|
|
59
59
|
super()
|
|
60
|
-
this.#timeoutDelay = timeoutDelay
|
|
61
|
-
this.#log = debug(`automerge-repo:dochandle:${this.documentId.slice(0, 5)}`)
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
this.documentId = documentId
|
|
62
|
+
|
|
63
|
+
if ("timeoutDelay" in options && options.timeoutDelay) {
|
|
64
|
+
this.#timeoutDelay = options.timeoutDelay
|
|
65
|
+
}
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
let doc: T
|
|
68
|
+
const isNew = "isNew" in options && options.isNew
|
|
67
69
|
if (isNew) {
|
|
68
|
-
|
|
70
|
+
// T should really be constrained to extend `Record<string, unknown>` (an automerge doc can't be
|
|
71
|
+
// e.g. a primitive, an array, etc. - it must be an object). But adding that constraint creates
|
|
72
|
+
// a bunch of other problems elsewhere so for now we'll just cast it here to make Automerge happy.
|
|
73
|
+
doc = A.from(options.initialValue as Record<string, unknown>) as T
|
|
74
|
+
doc = A.emptyChange<T>(doc)
|
|
75
|
+
} else {
|
|
76
|
+
doc = A.init<T>()
|
|
69
77
|
}
|
|
70
78
|
|
|
79
|
+
this.#log = debug(`automerge-repo:dochandle:${this.documentId.slice(0, 5)}`)
|
|
80
|
+
|
|
71
81
|
/**
|
|
72
82
|
* Internally we use a state machine to orchestrate document loading and/or syncing, in order to
|
|
73
83
|
* avoid requesting data we already have, or surfacing intermediate values to the consumer.
|
|
@@ -451,10 +461,22 @@ export class DocHandle<T> //
|
|
|
451
461
|
// WRAPPER CLASS TYPES
|
|
452
462
|
|
|
453
463
|
/** @hidden */
|
|
454
|
-
export
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
464
|
+
export type DocHandleOptions<T> =
|
|
465
|
+
// NEW DOCUMENTS
|
|
466
|
+
| {
|
|
467
|
+
/** If we know this is a new document (because we're creating it) this should be set to true. */
|
|
468
|
+
isNew: true
|
|
469
|
+
|
|
470
|
+
/** The initial value of the document. */
|
|
471
|
+
initialValue?: T
|
|
472
|
+
}
|
|
473
|
+
// EXISTING DOCUMENTS
|
|
474
|
+
| {
|
|
475
|
+
isNew?: false
|
|
476
|
+
|
|
477
|
+
/** The number of milliseconds before we mark this document as unavailable if we don't have it and nobody shares it with us. */
|
|
478
|
+
timeoutDelay?: number
|
|
479
|
+
}
|
|
458
480
|
|
|
459
481
|
export interface DocHandleMessagePayload {
|
|
460
482
|
destinationId: PeerId
|
|
@@ -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 })
|