@automerge/automerge-repo 1.0.6 → 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 +7 -7
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +3 -7
- package/dist/EphemeralData.d.ts +2 -2
- package/dist/EphemeralData.d.ts.map +1 -1
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +7 -11
- 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 +9 -11
- package/src/EphemeralData.ts +2 -2
- package/src/Repo.ts +10 -14
- 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 +177 -97
- package/test/StorageSubsystem.test.ts +6 -9
- package/test/tsconfig.json +8 -0
- package/typedoc.json +3 -3
- package/.mocharc.json +0 -5
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
|
@@ -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
|
|
@@ -431,7 +429,7 @@ export class DocHandle<T> //
|
|
|
431
429
|
* a user could have multiple tabs open and would appear as multiple PeerIds.
|
|
432
430
|
* every message source must have a unique PeerId.
|
|
433
431
|
*/
|
|
434
|
-
broadcast(message:
|
|
432
|
+
broadcast(message: unknown) {
|
|
435
433
|
this.emit("ephemeral-message-outbound", {
|
|
436
434
|
handle: this,
|
|
437
435
|
data: encode(message),
|
|
@@ -474,14 +472,14 @@ export interface DocHandleChangePayload<T> {
|
|
|
474
472
|
patchInfo: A.PatchInfo<T>
|
|
475
473
|
}
|
|
476
474
|
|
|
477
|
-
export interface DocHandleEphemeralMessagePayload {
|
|
478
|
-
handle: DocHandle<
|
|
475
|
+
export interface DocHandleEphemeralMessagePayload<T> {
|
|
476
|
+
handle: DocHandle<T>
|
|
479
477
|
senderId: PeerId
|
|
480
478
|
message: unknown
|
|
481
479
|
}
|
|
482
480
|
|
|
483
|
-
export interface DocHandleOutboundEphemeralMessagePayload {
|
|
484
|
-
handle: DocHandle<
|
|
481
|
+
export interface DocHandleOutboundEphemeralMessagePayload<T> {
|
|
482
|
+
handle: DocHandle<T>
|
|
485
483
|
data: Uint8Array
|
|
486
484
|
}
|
|
487
485
|
|
|
@@ -490,9 +488,9 @@ export interface DocHandleEvents<T> {
|
|
|
490
488
|
change: (payload: DocHandleChangePayload<T>) => void
|
|
491
489
|
delete: (payload: DocHandleDeletePayload<T>) => void
|
|
492
490
|
unavailable: (payload: DocHandleDeletePayload<T>) => void
|
|
493
|
-
"ephemeral-message": (payload: DocHandleEphemeralMessagePayload) => void
|
|
491
|
+
"ephemeral-message": (payload: DocHandleEphemeralMessagePayload<T>) => void
|
|
494
492
|
"ephemeral-message-outbound": (
|
|
495
|
-
payload: DocHandleOutboundEphemeralMessagePayload
|
|
493
|
+
payload: DocHandleOutboundEphemeralMessagePayload<T>
|
|
496
494
|
) => void
|
|
497
495
|
}
|
|
498
496
|
|
package/src/EphemeralData.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DocumentId, PeerId } from "./index.js"
|
|
2
|
-
import {
|
|
2
|
+
import { EphemeralMessage, MessageContents } from "./network/messages.js"
|
|
3
3
|
|
|
4
4
|
// types
|
|
5
5
|
/** A randomly generated string created when the {@link Repo} starts up */
|
|
@@ -12,6 +12,6 @@ export interface EphemeralDataPayload {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export type EphemeralDataMessageEvents = {
|
|
15
|
-
message: (event:
|
|
15
|
+
message: (event: MessageContents<EphemeralMessage>) => void
|
|
16
16
|
data: (event: EphemeralDataPayload) => void
|
|
17
17
|
}
|
package/src/Repo.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { StorageAdapter } from "./storage/StorageAdapter.js"
|
|
|
5
5
|
import { StorageSubsystem } from "./storage/StorageSubsystem.js"
|
|
6
6
|
import { CollectionSynchronizer } from "./synchronizer/CollectionSynchronizer.js"
|
|
7
7
|
import { type AutomergeUrl, DocumentId, PeerId } from "./types.js"
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
import {
|
|
10
10
|
parseAutomergeUrl,
|
|
11
11
|
generateAutomergeUrl,
|
|
@@ -106,9 +106,9 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
106
106
|
// The synchronizer uses the network subsystem to keep documents in sync with peers.
|
|
107
107
|
const synchronizer = new CollectionSynchronizer(this)
|
|
108
108
|
|
|
109
|
-
// When the synchronizer emits
|
|
109
|
+
// When the synchronizer emits messages, send them to peers
|
|
110
110
|
synchronizer.on("message", message => {
|
|
111
|
-
this.#log(`sending
|
|
111
|
+
this.#log(`sending ${message.type} message to ${message.targetId}`)
|
|
112
112
|
networkSubsystem.send(message)
|
|
113
113
|
})
|
|
114
114
|
|
|
@@ -198,9 +198,9 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
198
198
|
* @param clonedHandle - The handle to clone
|
|
199
199
|
*
|
|
200
200
|
* @remarks This is a wrapper around the `clone` function in the Automerge library.
|
|
201
|
-
* The new `DocHandle` will have a new URL but will share history with the original,
|
|
202
|
-
* which means that changes made to the cloned handle can be sensibly merged back
|
|
203
|
-
* into the original.
|
|
201
|
+
* The new `DocHandle` will have a new URL but will share history with the original,
|
|
202
|
+
* which means that changes made to the cloned handle can be sensibly merged back
|
|
203
|
+
* into the original.
|
|
204
204
|
*
|
|
205
205
|
* Any peers this `Repo` is connected to for whom `sharePolicy` returns `true` will
|
|
206
206
|
* be notified of the newly created DocHandle.
|
|
@@ -223,7 +223,7 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
223
223
|
|
|
224
224
|
const handle = this.create<T>()
|
|
225
225
|
|
|
226
|
-
handle.update((
|
|
226
|
+
handle.update(() => {
|
|
227
227
|
// we replace the document with the new cloned one
|
|
228
228
|
return Automerge.clone(sourceDoc)
|
|
229
229
|
})
|
|
@@ -240,7 +240,7 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
240
240
|
automergeUrl: AutomergeUrl
|
|
241
241
|
): DocHandle<T> {
|
|
242
242
|
if (!isValidAutomergeUrl(automergeUrl)) {
|
|
243
|
-
|
|
243
|
+
const maybeAutomergeUrl = parseLegacyUUID(automergeUrl)
|
|
244
244
|
if (maybeAutomergeUrl) {
|
|
245
245
|
console.warn(
|
|
246
246
|
"Legacy UUID document ID detected, converting to AutomergeUrl. This will be removed in a future version."
|
|
@@ -274,17 +274,13 @@ export class Repo extends EventEmitter<RepoEvents> {
|
|
|
274
274
|
/** The documentId of the handle to delete */
|
|
275
275
|
id: DocumentId | AutomergeUrl
|
|
276
276
|
) {
|
|
277
|
-
if (isValidAutomergeUrl(id))
|
|
278
|
-
;({ documentId: id } = parseAutomergeUrl(id))
|
|
279
|
-
}
|
|
277
|
+
if (isValidAutomergeUrl(id)) id = parseAutomergeUrl(id).documentId
|
|
280
278
|
|
|
281
279
|
const handle = this.#getHandle(id, false)
|
|
282
280
|
handle.delete()
|
|
283
281
|
|
|
284
282
|
delete this.#handleCache[id]
|
|
285
|
-
this.emit("delete-document", {
|
|
286
|
-
documentId: id,
|
|
287
|
-
})
|
|
283
|
+
this.emit("delete-document", { documentId: id })
|
|
288
284
|
}
|
|
289
285
|
}
|
|
290
286
|
|
package/src/helpers/cbor.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Encoder, decode as cborXdecode } from "cbor-x"
|
|
1
|
+
import { Encoder, decode as cborXdecode } from "cbor-x"
|
|
2
2
|
|
|
3
|
-
export function encode(obj:
|
|
4
|
-
|
|
3
|
+
export function encode(obj: unknown): Buffer {
|
|
4
|
+
const encoder = new Encoder({ tagUint8Array: false })
|
|
5
5
|
return encoder.encode(obj)
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export function decode(buf: Buffer | Uint8Array):
|
|
8
|
+
export function decode<T = unknown>(buf: Buffer | Uint8Array): T {
|
|
9
9
|
return cborXdecode(buf)
|
|
10
10
|
}
|
package/src/helpers/pause.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
export const pause = (t = 0) =>
|
|
2
2
|
new Promise<void>(resolve => setTimeout(() => resolve(), t))
|
|
3
3
|
|
|
4
|
-
export function rejectOnTimeout<T>(
|
|
4
|
+
export function rejectOnTimeout<T>(
|
|
5
|
+
promise: Promise<T>,
|
|
6
|
+
millis: number
|
|
7
|
+
): Promise<T> {
|
|
5
8
|
return Promise.race([
|
|
6
9
|
promise,
|
|
7
|
-
pause(millis).then(() => {
|
|
10
|
+
pause(millis).then(() => {
|
|
11
|
+
throw new Error("timeout exceeded")
|
|
12
|
+
}),
|
|
8
13
|
])
|
|
9
14
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import assert from "assert"
|
|
2
|
+
import { describe, it } from "vitest"
|
|
3
|
+
import { PeerId, Repo, type NetworkAdapter } from "../../index.js"
|
|
2
4
|
import { eventPromise, eventPromises } from "../eventPromise.js"
|
|
3
|
-
import { assert } from "chai"
|
|
4
|
-
import { describe, it } from "mocha"
|
|
5
5
|
import { pause } from "../pause.js"
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -6,7 +6,7 @@ export const withTimeout = async <T>(
|
|
|
6
6
|
promise: Promise<T>,
|
|
7
7
|
t: number
|
|
8
8
|
): Promise<T> => {
|
|
9
|
-
let timeoutId: ReturnType<typeof setTimeout>
|
|
9
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
|
10
10
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
11
11
|
timeoutId = setTimeout(
|
|
12
12
|
() => reject(new TimeoutError(`withTimeout: timed out after ${t}ms`)),
|
|
@@ -16,7 +16,7 @@ export const withTimeout = async <T>(
|
|
|
16
16
|
try {
|
|
17
17
|
return await Promise.race([promise, timeoutPromise])
|
|
18
18
|
} finally {
|
|
19
|
-
clearTimeout(timeoutId
|
|
19
|
+
clearTimeout(timeoutId)
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
package/src/index.ts
CHANGED
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
*
|
|
17
17
|
* ```typescript
|
|
18
18
|
* import { Repo } from "@automerge/automerge-repo";
|
|
19
|
-
*
|
|
19
|
+
*
|
|
20
20
|
* const repo = new Repo({
|
|
21
|
-
* storage: <storage adapter>,
|
|
21
|
+
* storage: <storage adapter>,
|
|
22
22
|
* network: [<network adapter>, <network adapter>]
|
|
23
23
|
* })
|
|
24
24
|
*
|
|
@@ -26,47 +26,54 @@
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
export {
|
|
30
|
-
export
|
|
29
|
+
export { Repo } from "./Repo.js"
|
|
30
|
+
export { DocHandle } from "./DocHandle.js"
|
|
31
|
+
export { NetworkAdapter } from "./network/NetworkAdapter.js"
|
|
32
|
+
export { StorageAdapter } from "./storage/StorageAdapter.js"
|
|
33
|
+
export {
|
|
34
|
+
isValidAutomergeUrl,
|
|
35
|
+
parseAutomergeUrl,
|
|
36
|
+
stringifyAutomergeUrl,
|
|
37
|
+
} from "./DocUrl.js"
|
|
38
|
+
export { isValidRepoMessage } from "./network/messages.js"
|
|
39
|
+
|
|
40
|
+
/** @hidden **/
|
|
41
|
+
export * as cbor from "./helpers/cbor.js"
|
|
42
|
+
|
|
43
|
+
// types
|
|
44
|
+
|
|
45
|
+
export type {
|
|
31
46
|
DocHandleChangePayload,
|
|
32
47
|
DocHandleDeletePayload,
|
|
48
|
+
DocHandleEncodedChangePayload,
|
|
33
49
|
DocHandleEphemeralMessagePayload,
|
|
50
|
+
DocHandleEvents,
|
|
51
|
+
DocHandleOptions,
|
|
34
52
|
DocHandleOutboundEphemeralMessagePayload,
|
|
35
|
-
|
|
53
|
+
HandleState,
|
|
36
54
|
} from "./DocHandle.js"
|
|
37
|
-
export { NetworkAdapter } from "./network/NetworkAdapter.js"
|
|
38
55
|
export type {
|
|
56
|
+
DeleteDocumentPayload,
|
|
57
|
+
DocumentPayload,
|
|
58
|
+
RepoConfig,
|
|
59
|
+
RepoEvents,
|
|
60
|
+
SharePolicy,
|
|
61
|
+
} from "./Repo.js"
|
|
62
|
+
export type {
|
|
63
|
+
NetworkAdapterEvents,
|
|
39
64
|
OpenPayload,
|
|
40
65
|
PeerCandidatePayload,
|
|
41
66
|
PeerDisconnectedPayload,
|
|
42
|
-
NetworkAdapterEvents,
|
|
43
67
|
} from "./network/NetworkAdapter.js"
|
|
44
|
-
|
|
45
|
-
// This is a bit confusing right now, but:
|
|
46
|
-
// Message is the type for messages used outside of the network adapters
|
|
47
|
-
// there are some extra internal network adapter-only messages on NetworkAdapterMessage
|
|
48
|
-
// and Message is (as of this writing) a union type for EphmeralMessage and SyncMessage
|
|
49
68
|
export type {
|
|
50
|
-
Message,
|
|
51
69
|
ArriveMessage,
|
|
52
|
-
|
|
53
|
-
NetworkAdapterMessage,
|
|
70
|
+
DocumentUnavailableMessage,
|
|
54
71
|
EphemeralMessage,
|
|
72
|
+
Message,
|
|
73
|
+
RepoMessage,
|
|
55
74
|
RequestMessage,
|
|
56
|
-
DocumentUnavailableMessage,
|
|
57
75
|
SyncMessage,
|
|
58
|
-
|
|
76
|
+
WelcomeMessage,
|
|
59
77
|
} from "./network/messages.js"
|
|
60
|
-
export {
|
|
61
|
-
|
|
62
|
-
export { Repo, type SharePolicy, type RepoConfig, type RepoEvents, type DeleteDocumentPayload, type DocumentPayload } from "./Repo.js"
|
|
63
|
-
export { StorageAdapter, type StorageKey } from "./storage/StorageAdapter.js"
|
|
64
|
-
export {
|
|
65
|
-
parseAutomergeUrl,
|
|
66
|
-
isValidAutomergeUrl,
|
|
67
|
-
stringifyAutomergeUrl as generateAutomergeUrl,
|
|
68
|
-
} from "./DocUrl.js"
|
|
78
|
+
export type { StorageKey } from "./storage/StorageAdapter.js"
|
|
69
79
|
export * from "./types.js"
|
|
70
|
-
|
|
71
|
-
/** @hidden **/
|
|
72
|
-
export * as cbor from "./helpers/cbor.js"
|