@automerge/automerge-repo 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +1 -1
- package/dist/DocHandle.d.ts +20 -7
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +27 -7
- package/dist/EphemeralData.d.ts +2 -2
- package/dist/EphemeralData.d.ts.map +1 -1
- package/dist/Repo.d.ts +16 -0
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +38 -10
- package/dist/helpers/cbor.d.ts +2 -2
- package/dist/helpers/cbor.d.ts.map +1 -1
- package/dist/helpers/cbor.js +1 -1
- package/dist/helpers/pause.d.ts.map +1 -1
- package/dist/helpers/pause.js +3 -1
- package/dist/helpers/tests/network-adapter-tests.d.ts.map +1 -1
- package/dist/helpers/tests/network-adapter-tests.js +2 -2
- package/dist/index.d.ts +11 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/network/NetworkAdapter.d.ts +3 -3
- package/dist/network/NetworkAdapter.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.d.ts +2 -2
- package/dist/network/NetworkSubsystem.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.js +30 -18
- package/dist/network/messages.d.ts +38 -68
- package/dist/network/messages.d.ts.map +1 -1
- package/dist/network/messages.js +13 -21
- package/dist/storage/StorageSubsystem.js +7 -7
- package/dist/synchronizer/CollectionSynchronizer.d.ts +3 -3
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +2 -2
- package/dist/synchronizer/DocSynchronizer.d.ts +3 -3
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +22 -29
- package/dist/synchronizer/Synchronizer.d.ts +2 -2
- package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -13
- package/src/DocHandle.ts +38 -14
- package/src/EphemeralData.ts +2 -2
- package/src/Repo.ts +46 -12
- package/src/helpers/cbor.ts +4 -4
- package/src/helpers/pause.ts +7 -2
- package/src/helpers/tests/network-adapter-tests.ts +3 -3
- package/src/helpers/withTimeout.ts +2 -2
- package/src/index.ts +36 -29
- package/src/network/NetworkAdapter.ts +7 -3
- package/src/network/NetworkSubsystem.ts +31 -23
- package/src/network/messages.ts +88 -151
- package/src/storage/StorageSubsystem.ts +8 -8
- package/src/synchronizer/CollectionSynchronizer.ts +6 -15
- package/src/synchronizer/DocSynchronizer.ts +34 -48
- package/src/synchronizer/Synchronizer.ts +2 -2
- package/src/types.ts +8 -3
- package/test/CollectionSynchronizer.test.ts +58 -53
- package/test/DocHandle.test.ts +35 -36
- package/test/DocSynchronizer.test.ts +9 -8
- package/test/Network.test.ts +1 -0
- package/test/Repo.test.ts +273 -88
- package/test/StorageSubsystem.test.ts +6 -9
- package/test/tsconfig.json +8 -0
- package/test/types.ts +2 -0
- package/typedoc.json +3 -3
- package/.mocharc.json +0 -5
|
@@ -1,45 +1,18 @@
|
|
|
1
|
-
import { SessionId } from "../
|
|
2
|
-
export { type SessionId } from "../EphemeralData.js";
|
|
3
|
-
import { DocumentId, PeerId } from "../types.js";
|
|
4
|
-
export declare function isValidMessage(message: NetworkAdapterMessage): message is SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage;
|
|
5
|
-
export declare function isDocumentUnavailableMessage(message: NetworkAdapterMessage): message is DocumentUnavailableMessage;
|
|
6
|
-
export declare function isRequestMessage(message: NetworkAdapterMessage): message is RequestMessage;
|
|
7
|
-
export declare function isSyncMessage(message: NetworkAdapterMessage): message is SyncMessage;
|
|
8
|
-
export declare function isEphemeralMessage(message: NetworkAdapterMessage | MessageContents): message is EphemeralMessage | EphemeralMessageContents;
|
|
9
|
-
export interface SyncMessageEnvelope {
|
|
10
|
-
senderId: PeerId;
|
|
11
|
-
}
|
|
12
|
-
export interface SyncMessageContents {
|
|
13
|
-
type: "sync";
|
|
14
|
-
data: Uint8Array;
|
|
15
|
-
targetId: PeerId;
|
|
16
|
-
documentId: DocumentId;
|
|
17
|
-
}
|
|
1
|
+
import { DocumentId, PeerId, SessionId } from "../types.js";
|
|
18
2
|
/**
|
|
19
3
|
* A sync message for a particular document
|
|
20
4
|
*/
|
|
21
5
|
export type SyncMessage = {
|
|
6
|
+
type: "sync";
|
|
22
7
|
/** The peer ID of the sender of this message */
|
|
23
8
|
senderId: PeerId;
|
|
24
|
-
type: "sync";
|
|
25
|
-
/** The automerge sync message */
|
|
26
|
-
data: Uint8Array;
|
|
27
9
|
/** The peer ID of the recipient of this message */
|
|
28
10
|
targetId: PeerId;
|
|
11
|
+
/** The automerge sync message */
|
|
12
|
+
data: Uint8Array;
|
|
29
13
|
/** The document ID of the document this message is for */
|
|
30
14
|
documentId: DocumentId;
|
|
31
15
|
};
|
|
32
|
-
export interface EphemeralMessageEnvelope {
|
|
33
|
-
senderId: PeerId;
|
|
34
|
-
count: number;
|
|
35
|
-
sessionId: SessionId;
|
|
36
|
-
}
|
|
37
|
-
export interface EphemeralMessageContents {
|
|
38
|
-
type: "ephemeral";
|
|
39
|
-
targetId: PeerId;
|
|
40
|
-
documentId: DocumentId;
|
|
41
|
-
data: Uint8Array;
|
|
42
|
-
}
|
|
43
16
|
/** An ephemeral message
|
|
44
17
|
*
|
|
45
18
|
* @remarks
|
|
@@ -51,41 +24,30 @@ export interface EphemeralMessageContents {
|
|
|
51
24
|
* we have already seen.
|
|
52
25
|
* */
|
|
53
26
|
export type EphemeralMessage = {
|
|
54
|
-
|
|
27
|
+
type: "ephemeral";
|
|
28
|
+
/** The peer ID of the sender of this message */
|
|
55
29
|
senderId: PeerId;
|
|
30
|
+
/** The peer ID of the recipient of this message */
|
|
31
|
+
targetId: PeerId;
|
|
56
32
|
/** A sequence number which must be incremented for each message sent by this peer */
|
|
57
33
|
count: number;
|
|
58
34
|
/** The ID of the session this message is part of. The sequence number for a given session always increases */
|
|
59
35
|
sessionId: SessionId;
|
|
60
|
-
type: "ephemeral";
|
|
61
|
-
/** The peer this message is for */
|
|
62
|
-
targetId: PeerId;
|
|
63
36
|
/** The document ID this message pertains to */
|
|
64
37
|
documentId: DocumentId;
|
|
65
38
|
/** The actual data of the message */
|
|
66
39
|
data: Uint8Array;
|
|
67
40
|
};
|
|
68
|
-
export interface DocumentUnavailableMessageContents {
|
|
69
|
-
type: "doc-unavailable";
|
|
70
|
-
documentId: DocumentId;
|
|
71
|
-
targetId: PeerId;
|
|
72
|
-
}
|
|
73
41
|
/** Sent by a {@link Repo} to indicate that it does not have the document and none of it's connected peers do either */
|
|
74
42
|
export type DocumentUnavailableMessage = {
|
|
75
|
-
/** The peer who sent this message */
|
|
76
|
-
senderId: PeerId;
|
|
77
43
|
type: "doc-unavailable";
|
|
44
|
+
/** The peer ID of the sender of this message */
|
|
45
|
+
senderId: PeerId;
|
|
46
|
+
/** The peer ID of the recipient of this message */
|
|
47
|
+
targetId: PeerId;
|
|
78
48
|
/** The document which the peer claims it doesn't have */
|
|
79
49
|
documentId: DocumentId;
|
|
80
|
-
/** The peer this message is for */
|
|
81
|
-
targetId: PeerId;
|
|
82
50
|
};
|
|
83
|
-
export interface RequestMessageContents {
|
|
84
|
-
type: "request";
|
|
85
|
-
data: Uint8Array;
|
|
86
|
-
targetId: PeerId;
|
|
87
|
-
documentId: DocumentId;
|
|
88
|
-
}
|
|
89
51
|
/** Sent by a {@link Repo} to request a document from a peer
|
|
90
52
|
*
|
|
91
53
|
* @remarks
|
|
@@ -93,40 +55,48 @@ export interface RequestMessageContents {
|
|
|
93
55
|
* as the initial sync message when asking the other peer if it has the document.
|
|
94
56
|
* */
|
|
95
57
|
export type RequestMessage = {
|
|
96
|
-
/** The peer who sent this message */
|
|
97
|
-
senderId: PeerId;
|
|
98
58
|
type: "request";
|
|
59
|
+
/** The peer ID of the sender of this message */
|
|
60
|
+
senderId: PeerId;
|
|
61
|
+
/** The peer ID of the recipient of this message */
|
|
62
|
+
targetId: PeerId;
|
|
99
63
|
/** The initial automerge sync message */
|
|
100
64
|
data: Uint8Array;
|
|
101
|
-
/** The peer this message is for */
|
|
102
|
-
targetId: PeerId;
|
|
103
65
|
/** The document ID this message requests */
|
|
104
66
|
documentId: DocumentId;
|
|
105
67
|
};
|
|
106
|
-
export type MessageContents = SyncMessageContents | EphemeralMessageContents | RequestMessageContents | DocumentUnavailableMessageContents;
|
|
107
|
-
/** The type of messages that {@link Repo} sends and receive to {@link NetworkAdapter}s */
|
|
108
|
-
export type Message = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage;
|
|
109
|
-
export type SynchronizerMessage = SyncMessage | RequestMessage | DocumentUnavailableMessage | EphemeralMessage;
|
|
110
68
|
/** Notify the network that we have arrived so everyone knows our peer ID */
|
|
111
69
|
export type ArriveMessage = {
|
|
112
|
-
/** Our peer ID */
|
|
113
|
-
senderId: PeerId;
|
|
114
70
|
type: "arrive";
|
|
71
|
+
/** The peer ID of the sender of this message */
|
|
72
|
+
senderId: PeerId;
|
|
73
|
+
/** Arrive messages don't have a targetId */
|
|
74
|
+
targetId: never;
|
|
115
75
|
};
|
|
116
76
|
/** Respond to an arriving peer with our peer ID */
|
|
117
77
|
export type WelcomeMessage = {
|
|
118
|
-
|
|
78
|
+
type: "welcome";
|
|
79
|
+
/** The peer ID of the recipient sender this message */
|
|
119
80
|
senderId: PeerId;
|
|
120
|
-
/** The ID of the
|
|
81
|
+
/** The peer ID of the recipient of this message */
|
|
121
82
|
targetId: PeerId;
|
|
122
|
-
type: "welcome";
|
|
123
83
|
};
|
|
124
|
-
/**
|
|
84
|
+
/** These are message types that a {@link NetworkAdapter} surfaces to a {@link Repo}. */
|
|
85
|
+
export type RepoMessage = SyncMessage | EphemeralMessage | RequestMessage | DocumentUnavailableMessage;
|
|
86
|
+
/** These are all the message types that a {@link NetworkAdapter} might see.
|
|
125
87
|
*
|
|
126
88
|
* @remarks
|
|
127
|
-
* It is not _required_ that a {@link NetworkAdapter} use
|
|
128
|
-
*
|
|
129
|
-
* transport. However, this type is a useful default.
|
|
89
|
+
* It is not _required_ that a {@link NetworkAdapter} use these types: They are free to use
|
|
90
|
+
* whatever message type makes sense for their transport. However, this type is a useful default.
|
|
130
91
|
* */
|
|
131
|
-
export type
|
|
92
|
+
export type Message = RepoMessage | ArriveMessage | WelcomeMessage;
|
|
93
|
+
/**
|
|
94
|
+
* The contents of a message, without the sender ID or other properties added by the {@link NetworkSubsystem})
|
|
95
|
+
*/
|
|
96
|
+
export type MessageContents<T extends Message = Message> = T extends EphemeralMessage ? Omit<T, "senderId" | "count" | "sessionId"> : Omit<T, "senderId">;
|
|
97
|
+
export declare const isValidRepoMessage: (message: Message) => message is RepoMessage;
|
|
98
|
+
export declare const isDocumentUnavailableMessage: (msg: Message) => msg is DocumentUnavailableMessage;
|
|
99
|
+
export declare const isRequestMessage: (msg: Message) => msg is RequestMessage;
|
|
100
|
+
export declare const isSyncMessage: (msg: Message) => msg is SyncMessage;
|
|
101
|
+
export declare const isEphemeralMessage: (msg: Message) => msg is EphemeralMessage;
|
|
132
102
|
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/network/messages.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/network/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE3D;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IAEZ,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,iCAAiC;IACjC,IAAI,EAAE,UAAU,CAAA;IAEhB,0DAA0D;IAC1D,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED;;;;;;;;;KASK;AACL,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,WAAW,CAAA;IAEjB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAA;IAEb,8GAA8G;IAC9G,SAAS,EAAE,SAAS,CAAA;IAEpB,+CAA+C;IAC/C,UAAU,EAAE,UAAU,CAAA;IAEtB,qCAAqC;IACrC,IAAI,EAAE,UAAU,CAAA;CACjB,CAAA;AAED,uHAAuH;AACvH,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,iBAAiB,CAAA;IAEvB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,yDAAyD;IACzD,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED;;;;;KAKK;AACL,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAA;IAEf,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAEhB,yCAAyC;IACzC,IAAI,EAAE,UAAU,CAAA;IAEhB,4CAA4C;IAC5C,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,4EAA4E;AAC5E,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,QAAQ,CAAA;IAEd,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAEhB,4CAA4C;IAC5C,QAAQ,EAAE,KAAK,CAAA;CAChB,CAAA;AAED,mDAAmD;AACnD,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAA;IAEf,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAA;IAEhB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,wFAAwF;AACxF,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,gBAAgB,GAChB,cAAc,GACd,0BAA0B,CAAA;AAE9B;;;;;KAKK;AACL,MAAM,MAAM,OAAO,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAA;AAElE;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IACrD,CAAC,SAAS,gBAAgB,GACtB,IAAI,CAAC,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,WAAW,CAAC,GAC3C,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;AAIzB,eAAO,MAAM,kBAAkB,YAAa,OAAO,2BAOT,CAAA;AAG1C,eAAO,MAAM,4BAA4B,QAAS,OAAO,sCACzB,CAAA;AAEhC,eAAO,MAAM,gBAAgB,QAAS,OAAO,0BACrB,CAAA;AAExB,eAAO,MAAM,aAAa,QAAS,OAAO,uBACrB,CAAA;AAErB,eAAO,MAAM,kBAAkB,QAAS,OAAO,4BACrB,CAAA"}
|
package/dist/network/messages.js
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export
|
|
14
|
-
return message.type === "request";
|
|
15
|
-
}
|
|
16
|
-
export function isSyncMessage(message) {
|
|
17
|
-
return message.type === "sync";
|
|
18
|
-
}
|
|
19
|
-
export function isEphemeralMessage(message) {
|
|
20
|
-
return message.type === "ephemeral";
|
|
21
|
-
}
|
|
1
|
+
// TYPE GUARDS
|
|
2
|
+
export const isValidRepoMessage = (message) => typeof message === "object" &&
|
|
3
|
+
typeof message.type === "string" &&
|
|
4
|
+
typeof message.senderId === "string" &&
|
|
5
|
+
(isSyncMessage(message) ||
|
|
6
|
+
isEphemeralMessage(message) ||
|
|
7
|
+
isRequestMessage(message) ||
|
|
8
|
+
isDocumentUnavailableMessage(message));
|
|
9
|
+
// prettier-ignore
|
|
10
|
+
export const isDocumentUnavailableMessage = (msg) => msg.type === "doc-unavailable";
|
|
11
|
+
export const isRequestMessage = (msg) => msg.type === "request";
|
|
12
|
+
export const isSyncMessage = (msg) => msg.type === "sync";
|
|
13
|
+
export const isEphemeralMessage = (msg) => msg.type === "ephemeral";
|
|
@@ -10,8 +10,8 @@ function keyHash(binary) {
|
|
|
10
10
|
return hashHex;
|
|
11
11
|
}
|
|
12
12
|
function headsHash(heads) {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const encoder = new TextEncoder();
|
|
14
|
+
const headsbinary = mergeArrays(heads.map((h) => encoder.encode(h)));
|
|
15
15
|
return keyHash(headsbinary);
|
|
16
16
|
}
|
|
17
17
|
export class StorageSubsystem {
|
|
@@ -89,18 +89,18 @@ export class StorageSubsystem {
|
|
|
89
89
|
if (!this.#shouldSave(documentId, doc)) {
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
const sourceChunks = this.#chunkInfos.get(documentId) ?? [];
|
|
93
93
|
if (this.#shouldCompact(sourceChunks)) {
|
|
94
|
-
this.#saveTotal(documentId, doc, sourceChunks);
|
|
94
|
+
void this.#saveTotal(documentId, doc, sourceChunks);
|
|
95
95
|
}
|
|
96
96
|
else {
|
|
97
|
-
this.#saveIncremental(documentId, doc);
|
|
97
|
+
void this.#saveIncremental(documentId, doc);
|
|
98
98
|
}
|
|
99
99
|
this.#storedHeads.set(documentId, A.getHeads(doc));
|
|
100
100
|
}
|
|
101
101
|
async remove(documentId) {
|
|
102
|
-
this.#storageAdapter.removeRange([documentId, "snapshot"]);
|
|
103
|
-
this.#storageAdapter.removeRange([documentId, "incremental"]);
|
|
102
|
+
void this.#storageAdapter.removeRange([documentId, "snapshot"]);
|
|
103
|
+
void this.#storageAdapter.removeRange([documentId, "incremental"]);
|
|
104
104
|
}
|
|
105
105
|
#shouldSave(documentId, doc) {
|
|
106
106
|
const oldHeads = this.#storedHeads.get(documentId);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Repo } from "../Repo.js";
|
|
2
|
-
import {
|
|
2
|
+
import { DocumentId, PeerId } from "../types.js";
|
|
3
3
|
import { Synchronizer } from "./Synchronizer.js";
|
|
4
|
-
import {
|
|
4
|
+
import { RepoMessage } from "../network/messages.js";
|
|
5
5
|
/** A CollectionSynchronizer is responsible for synchronizing a DocCollection with peers. */
|
|
6
6
|
export declare class CollectionSynchronizer extends Synchronizer {
|
|
7
7
|
#private;
|
|
@@ -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: RepoMessage): 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":"
|
|
1
|
+
{"version":3,"file":"CollectionSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/CollectionSynchronizer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAGpD,4FAA4F;AAC5F,qBAAa,sBAAuB,SAAQ,YAAY;;IAU1C,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,IAAI;IAiC9B;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,WAAW;IAyBzC;;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;CAQ1B"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { stringifyAutomergeUrl
|
|
1
|
+
import { stringifyAutomergeUrl } from "../DocUrl.js";
|
|
2
2
|
import { DocSynchronizer } from "./DocSynchronizer.js";
|
|
3
3
|
import { Synchronizer } from "./Synchronizer.js";
|
|
4
4
|
import debug from "debug";
|
|
@@ -85,7 +85,7 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
85
85
|
this.#peers.add(peerId);
|
|
86
86
|
for (const docSynchronizer of Object.values(this.#docSynchronizers)) {
|
|
87
87
|
const { documentId } = docSynchronizer;
|
|
88
|
-
this.repo.sharePolicy(peerId, documentId).then(okToShare => {
|
|
88
|
+
void this.repo.sharePolicy(peerId, documentId).then(okToShare => {
|
|
89
89
|
if (okToShare)
|
|
90
90
|
docSynchronizer.beginSync([peerId]);
|
|
91
91
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DocHandle } from "../DocHandle.js";
|
|
2
|
+
import { EphemeralMessage, RepoMessage, RequestMessage, SyncMessage } from "../network/messages.js";
|
|
2
3
|
import { PeerId } from "../types.js";
|
|
3
4
|
import { Synchronizer } from "./Synchronizer.js";
|
|
4
|
-
import { EphemeralMessage, RequestMessage, SynchronizerMessage, SyncMessage } from "../network/messages.js";
|
|
5
5
|
type PeerDocumentStatus = "unknown" | "has" | "unavailable" | "wants";
|
|
6
6
|
/**
|
|
7
7
|
* DocSynchronizer takes a handle to an Automerge document, and receives & dispatches sync messages
|
|
@@ -10,13 +10,13 @@ type PeerDocumentStatus = "unknown" | "has" | "unavailable" | "wants";
|
|
|
10
10
|
export declare class DocSynchronizer extends Synchronizer {
|
|
11
11
|
#private;
|
|
12
12
|
private handle;
|
|
13
|
-
constructor(handle: DocHandle<
|
|
13
|
+
constructor(handle: DocHandle<unknown>);
|
|
14
14
|
get peerStates(): Record<PeerId, PeerDocumentStatus>;
|
|
15
15
|
get documentId(): import("../types.js").DocumentId;
|
|
16
16
|
hasPeer(peerId: PeerId): boolean;
|
|
17
17
|
beginSync(peerIds: PeerId[]): void;
|
|
18
18
|
endSync(peerId: PeerId): void;
|
|
19
|
-
receiveMessage(message:
|
|
19
|
+
receiveMessage(message: RepoMessage): void;
|
|
20
20
|
receiveEphemeralMessage(message: EphemeralMessage): void;
|
|
21
21
|
receiveSyncMessage(message: SyncMessage | RequestMessage): void;
|
|
22
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/DocSynchronizer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DocSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/DocSynchronizer.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,SAAS,EAKV,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAEL,gBAAgB,EAChB,WAAW,EAEX,cAAc,EACd,WAAW,EAEZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,KAAK,kBAAkB,GAAG,SAAS,GAAG,KAAK,GAAG,aAAa,GAAG,OAAO,CAAA;AAErE;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;;IAiBnC,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC;IAoB9C,IAAI,UAAU,uCAEb;IAED,IAAI,UAAU,qCAEb;IA8FD,OAAO,CAAC,MAAM,EAAE,MAAM;IAItB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IAmC3B,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;CA4EzD"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as A from "@automerge/automerge/next";
|
|
2
|
-
import {
|
|
3
|
-
import { Synchronizer } from "./Synchronizer.js";
|
|
2
|
+
import { decode } from "cbor-x";
|
|
4
3
|
import debug from "debug";
|
|
4
|
+
import { READY, REQUESTING, UNAVAILABLE, } from "../DocHandle.js";
|
|
5
5
|
import { isRequestMessage, } from "../network/messages.js";
|
|
6
|
-
import {
|
|
6
|
+
import { Synchronizer } from "./Synchronizer.js";
|
|
7
7
|
/**
|
|
8
8
|
* DocSynchronizer takes a handle to an Automerge document, and receives & dispatches sync messages
|
|
9
9
|
* to bring it inline with all other peers' versions.
|
|
@@ -49,18 +49,19 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
49
49
|
return;
|
|
50
50
|
this.#peers.forEach(peerId => this.#sendSyncMessage(peerId, doc));
|
|
51
51
|
}
|
|
52
|
-
async #broadcastToPeers({ data }) {
|
|
52
|
+
async #broadcastToPeers({ data, }) {
|
|
53
53
|
this.#log(`broadcastToPeers`, this.#peers);
|
|
54
54
|
this.#peers.forEach(peerId => this.#sendEphemeralMessage(peerId, data));
|
|
55
55
|
}
|
|
56
56
|
#sendEphemeralMessage(peerId, data) {
|
|
57
57
|
this.#log(`sendEphemeralMessage ->${peerId}`);
|
|
58
|
-
|
|
58
|
+
const message = {
|
|
59
59
|
type: "ephemeral",
|
|
60
60
|
targetId: peerId,
|
|
61
61
|
documentId: this.handle.documentId,
|
|
62
62
|
data,
|
|
63
|
-
}
|
|
63
|
+
};
|
|
64
|
+
this.emit("message", message);
|
|
64
65
|
}
|
|
65
66
|
#getSyncState(peerId) {
|
|
66
67
|
if (!this.#peers.includes(peerId)) {
|
|
@@ -85,10 +86,9 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
85
86
|
const [newSyncState, message] = A.generateSyncMessage(doc, syncState);
|
|
86
87
|
this.#setSyncState(peerId, newSyncState);
|
|
87
88
|
if (message) {
|
|
88
|
-
|
|
89
|
-
const decoded = A.decodeSyncMessage(message);
|
|
89
|
+
const isNew = A.getHeads(doc).length === 0;
|
|
90
90
|
if (!this.handle.isReady() &&
|
|
91
|
-
|
|
91
|
+
isNew &&
|
|
92
92
|
newSyncState.sharedHeads.length === 0 &&
|
|
93
93
|
!Object.values(this.#peerDocumentStatuses).includes("has") &&
|
|
94
94
|
this.#peerDocumentStatuses[peerId] === "unknown") {
|
|
@@ -109,30 +109,17 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
111
|
// if we have sent heads, then the peer now has or will have the document
|
|
112
|
-
if (
|
|
112
|
+
if (!isNew) {
|
|
113
113
|
this.#peerDocumentStatuses[peerId] = "has";
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
-
#logMessage = (label, message) => {
|
|
118
|
-
// This is real expensive...
|
|
119
|
-
return;
|
|
120
|
-
const size = message.byteLength;
|
|
121
|
-
const logText = `${label} ${size}b`;
|
|
122
|
-
const decoded = A.decodeSyncMessage(message);
|
|
123
|
-
this.#conciseLog(logText);
|
|
124
|
-
this.#log(logText, decoded);
|
|
125
|
-
// expanding is expensive, so only do it if we're logging at this level
|
|
126
|
-
const expanded = this.#opsLog.enabled
|
|
127
|
-
? decoded.changes.flatMap((change) => A.decodeChange(change).ops.map((op) => JSON.stringify(op)))
|
|
128
|
-
: null;
|
|
129
|
-
this.#opsLog(logText, expanded);
|
|
130
|
-
};
|
|
131
117
|
/// PUBLIC
|
|
132
118
|
hasPeer(peerId) {
|
|
133
119
|
return this.#peers.includes(peerId);
|
|
134
120
|
}
|
|
135
121
|
beginSync(peerIds) {
|
|
122
|
+
const newPeers = new Set(peerIds.filter(peerId => !this.#peers.includes(peerId)));
|
|
136
123
|
this.#log(`beginSync: ${peerIds.join(", ")}`);
|
|
137
124
|
// HACK: if we have a sync state already, we round-trip it through the encoding system to make
|
|
138
125
|
// sure state is preserved. This prevents an infinite loop caused by failed attempts to send
|
|
@@ -149,10 +136,15 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
149
136
|
// we register out peers first, then say that sync has started
|
|
150
137
|
this.#syncStarted = true;
|
|
151
138
|
this.#checkDocUnavailable();
|
|
152
|
-
|
|
139
|
+
const wasUnavailable = doc === undefined;
|
|
140
|
+
if (wasUnavailable && newPeers.size == 0) {
|
|
153
141
|
return;
|
|
142
|
+
}
|
|
143
|
+
// If the doc is unavailable we still need a blank document to generate
|
|
144
|
+
// the sync message from
|
|
145
|
+
const theDoc = doc ?? A.init();
|
|
154
146
|
peerIds.forEach(peerId => {
|
|
155
|
-
this.#sendSyncMessage(peerId,
|
|
147
|
+
this.#sendSyncMessage(peerId, theDoc);
|
|
156
148
|
});
|
|
157
149
|
});
|
|
158
150
|
}
|
|
@@ -181,7 +173,7 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
181
173
|
if (message.documentId !== this.handle.documentId)
|
|
182
174
|
throw new Error(`channelId doesn't match documentId`);
|
|
183
175
|
const { senderId, data } = message;
|
|
184
|
-
const contents = decode(data);
|
|
176
|
+
const contents = decode(new Uint8Array(data));
|
|
185
177
|
this.handle.emit("ephemeral-message", {
|
|
186
178
|
handle: this.handle,
|
|
187
179
|
senderId,
|
|
@@ -234,11 +226,12 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
234
226
|
this.#peers
|
|
235
227
|
.filter(peerId => this.#peerDocumentStatuses[peerId] === "wants")
|
|
236
228
|
.forEach(peerId => {
|
|
237
|
-
|
|
229
|
+
const message = {
|
|
238
230
|
type: "doc-unavailable",
|
|
239
231
|
documentId: this.handle.documentId,
|
|
240
232
|
targetId: peerId,
|
|
241
|
-
}
|
|
233
|
+
};
|
|
234
|
+
this.emit("message", message);
|
|
242
235
|
});
|
|
243
236
|
this.handle.unavailable();
|
|
244
237
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from "eventemitter3";
|
|
2
|
-
import {
|
|
2
|
+
import { RepoMessage, MessageContents } from "../network/messages.js";
|
|
3
3
|
export declare abstract class Synchronizer extends EventEmitter<SynchronizerEvents> {
|
|
4
|
-
abstract receiveMessage(message:
|
|
4
|
+
abstract receiveMessage(message: RepoMessage): void;
|
|
5
5
|
}
|
|
6
6
|
export interface SynchronizerEvents {
|
|
7
7
|
message: (arg: MessageContents) => void;
|
|
@@ -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,EAAE,
|
|
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,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAErE,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,GAAG,EAAE,eAAe,KAAK,IAAI,CAAA;CACxC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -20,6 +20,10 @@ export type BinaryDocumentId = Uint8Array & {
|
|
|
20
20
|
};
|
|
21
21
|
/** A branded type for peer IDs */
|
|
22
22
|
export type PeerId = string & {
|
|
23
|
-
__peerId:
|
|
23
|
+
__peerId: true;
|
|
24
|
+
};
|
|
25
|
+
/** A randomly generated string created when the {@link Repo} starts up */
|
|
26
|
+
export type SessionId = string & {
|
|
27
|
+
__SessionId: true;
|
|
24
28
|
};
|
|
25
29
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;GACG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAAE,YAAY,EAAE,IAAI,CAAA;CAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;GACG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAAE,YAAY,EAAE,IAAI,CAAA;CAAE,CAAA;AAExD;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,aAAa,EAAE,IAAI,CAAA;CAAE,CAAA;AAE3D;GACG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAAE,kBAAkB,EAAE,IAAI,CAAA;CAAE,CAAA;AAExE,kCAAkC;AAClC,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,CAAA;AAEhD,0EAA0E;AAC1E,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "A repository object to manage a collection of automerge documents",
|
|
5
5
|
"repository": "https://github.com/automerge/automerge-repo/tree/master/packages/automerge-repo",
|
|
6
6
|
"author": "Peter van Hardenberg <pvh@pvh.ca>",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"build": "tsc",
|
|
12
12
|
"watch": "npm-watch build",
|
|
13
13
|
"test:coverage": "c8 --reporter=lcov --reporter=html --reporter=text yarn test",
|
|
14
|
-
"test": "
|
|
14
|
+
"test": "vitest",
|
|
15
15
|
"test:watch": "npm-watch test",
|
|
16
16
|
"test:log": "cross-env DEBUG='automerge-repo:*' yarn test",
|
|
17
17
|
"fuzz": "ts-node --esm --experimentalSpecifierResolution=node fuzz/fuzz.ts"
|
|
@@ -20,18 +20,10 @@
|
|
|
20
20
|
"crypto": false
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"
|
|
24
|
-
"@types/debug": "^4.1.7",
|
|
25
|
-
"@types/node": "^20.4.8",
|
|
26
|
-
"@types/uuid": "^8.3.4",
|
|
27
|
-
"@types/ws": "^8.5.3",
|
|
28
|
-
"@typescript-eslint/eslint-plugin": "^5.33.0",
|
|
29
|
-
"@typescript-eslint/parser": "^5.33.0",
|
|
30
|
-
"http-server": "^14.1.0",
|
|
31
|
-
"typescript": "^5.1.6"
|
|
23
|
+
"http-server": "^14.1.0"
|
|
32
24
|
},
|
|
33
25
|
"peerDependencies": {
|
|
34
|
-
"@automerge/automerge": "^2.1.
|
|
26
|
+
"@automerge/automerge": "^2.1.5"
|
|
35
27
|
},
|
|
36
28
|
"dependencies": {
|
|
37
29
|
"bs58check": "^3.0.1",
|
|
@@ -65,5 +57,5 @@
|
|
|
65
57
|
"publishConfig": {
|
|
66
58
|
"access": "public"
|
|
67
59
|
},
|
|
68
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "71060981f168e511a99ab85b155a54a13fd04bcc"
|
|
69
61
|
}
|
package/src/DocHandle.ts
CHANGED
|
@@ -28,7 +28,7 @@ import { encode } from "./helpers/cbor.js"
|
|
|
28
28
|
* A `DocHandle` represents a document which is being managed by a {@link Repo}.
|
|
29
29
|
* To obtain `DocHandle` use {@link Repo.find} or {@link Repo.create}.
|
|
30
30
|
*
|
|
31
|
-
* To modify the underlying document use either {@link DocHandle.change} or
|
|
31
|
+
* To modify the underlying document use either {@link DocHandle.change} or
|
|
32
32
|
* {@link DocHandle.changeAt}. These methods will notify the `Repo` that some
|
|
33
33
|
* change has occured and the `Repo` will save any new changes to the
|
|
34
34
|
* attached {@link StorageAdapter} and send sync messages to connected peers.
|
|
@@ -290,14 +290,12 @@ export class DocHandle<T> //
|
|
|
290
290
|
async doc(
|
|
291
291
|
awaitStates: HandleState[] = [READY, UNAVAILABLE]
|
|
292
292
|
): Promise<A.Doc<T> | undefined> {
|
|
293
|
-
await pause() // yield one tick because reasons
|
|
294
293
|
try {
|
|
295
294
|
// wait for the document to enter one of the desired states
|
|
296
295
|
await this.#statePromise(awaitStates)
|
|
297
296
|
} catch (error) {
|
|
298
|
-
if (
|
|
299
|
-
|
|
300
|
-
else throw error
|
|
297
|
+
// if we timed out (or the load has already failed), return undefined
|
|
298
|
+
return undefined
|
|
301
299
|
}
|
|
302
300
|
// Return the document
|
|
303
301
|
return !this.isUnavailable() ? this.#doc : undefined
|
|
@@ -321,7 +319,7 @@ export class DocHandle<T> //
|
|
|
321
319
|
return this.#doc
|
|
322
320
|
}
|
|
323
321
|
|
|
324
|
-
/** `update` is called by the repo when we receive changes from the network
|
|
322
|
+
/** `update` is called by the repo when we receive changes from the network
|
|
325
323
|
* @hidden
|
|
326
324
|
* */
|
|
327
325
|
update(callback: (doc: A.Doc<T>) => A.Doc<T>) {
|
|
@@ -373,11 +371,37 @@ export class DocHandle<T> //
|
|
|
373
371
|
return resultHeads
|
|
374
372
|
}
|
|
375
373
|
|
|
374
|
+
/** Merge another document into this document
|
|
375
|
+
*
|
|
376
|
+
* @param otherHandle - the handle of the document to merge into this one
|
|
377
|
+
*
|
|
378
|
+
* @remarks
|
|
379
|
+
* This is a convenience method for
|
|
380
|
+
* `handle.change(doc => A.merge(doc, otherHandle.docSync()))`. Any peers
|
|
381
|
+
* whom we are sharing changes with will be notified of the changes resulting
|
|
382
|
+
* from the merge.
|
|
383
|
+
*
|
|
384
|
+
* @throws if either document is not ready or if `otherHandle` is unavailable (`otherHandle.docSync() === undefined`)
|
|
385
|
+
*/
|
|
386
|
+
merge(otherHandle: DocHandle<T>) {
|
|
387
|
+
if (!this.isReady() || !otherHandle.isReady()) {
|
|
388
|
+
throw new Error("Both handles must be ready to merge")
|
|
389
|
+
}
|
|
390
|
+
const mergingDoc = otherHandle.docSync()
|
|
391
|
+
if (!mergingDoc) {
|
|
392
|
+
throw new Error("The document to be merged in is null, aborting.")
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
this.update(doc => {
|
|
396
|
+
return A.merge(doc, mergingDoc)
|
|
397
|
+
})
|
|
398
|
+
}
|
|
399
|
+
|
|
376
400
|
unavailable() {
|
|
377
401
|
this.#machine.send(MARK_UNAVAILABLE)
|
|
378
402
|
}
|
|
379
403
|
|
|
380
|
-
/** `request` is called by the repo when the document is not found in storage
|
|
404
|
+
/** `request` is called by the repo when the document is not found in storage
|
|
381
405
|
* @hidden
|
|
382
406
|
* */
|
|
383
407
|
request() {
|
|
@@ -405,7 +429,7 @@ export class DocHandle<T> //
|
|
|
405
429
|
* a user could have multiple tabs open and would appear as multiple PeerIds.
|
|
406
430
|
* every message source must have a unique PeerId.
|
|
407
431
|
*/
|
|
408
|
-
broadcast(message:
|
|
432
|
+
broadcast(message: unknown) {
|
|
409
433
|
this.emit("ephemeral-message-outbound", {
|
|
410
434
|
handle: this,
|
|
411
435
|
data: encode(message),
|
|
@@ -448,14 +472,14 @@ export interface DocHandleChangePayload<T> {
|
|
|
448
472
|
patchInfo: A.PatchInfo<T>
|
|
449
473
|
}
|
|
450
474
|
|
|
451
|
-
export interface DocHandleEphemeralMessagePayload {
|
|
452
|
-
handle: DocHandle<
|
|
475
|
+
export interface DocHandleEphemeralMessagePayload<T> {
|
|
476
|
+
handle: DocHandle<T>
|
|
453
477
|
senderId: PeerId
|
|
454
478
|
message: unknown
|
|
455
479
|
}
|
|
456
480
|
|
|
457
|
-
export interface DocHandleOutboundEphemeralMessagePayload {
|
|
458
|
-
handle: DocHandle<
|
|
481
|
+
export interface DocHandleOutboundEphemeralMessagePayload<T> {
|
|
482
|
+
handle: DocHandle<T>
|
|
459
483
|
data: Uint8Array
|
|
460
484
|
}
|
|
461
485
|
|
|
@@ -464,9 +488,9 @@ export interface DocHandleEvents<T> {
|
|
|
464
488
|
change: (payload: DocHandleChangePayload<T>) => void
|
|
465
489
|
delete: (payload: DocHandleDeletePayload<T>) => void
|
|
466
490
|
unavailable: (payload: DocHandleDeletePayload<T>) => void
|
|
467
|
-
"ephemeral-message": (payload: DocHandleEphemeralMessagePayload) => void
|
|
491
|
+
"ephemeral-message": (payload: DocHandleEphemeralMessagePayload<T>) => void
|
|
468
492
|
"ephemeral-message-outbound": (
|
|
469
|
-
payload: DocHandleOutboundEphemeralMessagePayload
|
|
493
|
+
payload: DocHandleOutboundEphemeralMessagePayload<T>
|
|
470
494
|
) => void
|
|
471
495
|
}
|
|
472
496
|
|