@dxos/echo-pipeline 0.7.3-staging.cc8dd3e → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +16 -3
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +16 -3
- package/dist/lib/node/index.cjs.map +2 -2
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +16 -3
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/package.json +35 -34
- package/src/edge/echo-edge-replicator.test.ts +74 -31
- package/src/edge/echo-edge-replicator.ts +14 -3
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { getRandomPort } from 'get-port-please';
|
|
6
|
+
import { describe, expect, onTestFinished, test } from 'vitest';
|
|
6
7
|
|
|
7
8
|
import { Event } from '@dxos/async';
|
|
8
9
|
import { cbor } from '@dxos/automerge/automerge-repo';
|
|
@@ -16,69 +17,111 @@ import type { Peer } from '@dxos/protocols/proto/dxos/edge/messenger';
|
|
|
16
17
|
import { openAndClose } from '@dxos/test-utils';
|
|
17
18
|
|
|
18
19
|
import { EchoEdgeReplicator } from './echo-edge-replicator';
|
|
19
|
-
import type { EchoReplicatorContext } from '../automerge';
|
|
20
|
+
import type { EchoReplicatorContext, ReplicatorConnection } from '../automerge';
|
|
20
21
|
|
|
21
22
|
describe('EchoEdgeReplicator', () => {
|
|
22
|
-
test('reconnects', async (
|
|
23
|
-
const {
|
|
24
|
-
onTestFinished(cleanup);
|
|
25
|
-
const client = new EdgeClient(await createEphemeralEdgeIdentity(), { socketEndpoint: endpoint });
|
|
26
|
-
await openAndClose(client);
|
|
23
|
+
test('reconnects', async () => {
|
|
24
|
+
const { client, server } = await createClientServer();
|
|
27
25
|
|
|
28
26
|
const spaceId = SpaceId.random();
|
|
29
27
|
|
|
30
|
-
const replicator = new EchoEdgeReplicator({ edgeConnection: client });
|
|
31
28
|
const { context, connectionOpen } = createMockContext();
|
|
32
|
-
await
|
|
29
|
+
const replicator = await connectReplicator(client, context);
|
|
33
30
|
await replicator.connectToSpace(spaceId);
|
|
34
31
|
|
|
35
32
|
client.setIdentity(await createEphemeralEdgeIdentity());
|
|
36
33
|
await connectionOpen.waitForCount(1);
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
{
|
|
41
|
-
identityKey: client.identityKey,
|
|
42
|
-
peerKey: client.peerKey,
|
|
43
|
-
},
|
|
44
|
-
spaceId,
|
|
45
|
-
),
|
|
46
|
-
);
|
|
35
|
+
const forbidden = createForbiddenMessage({ identityKey: client.identityKey, peerKey: client.peerKey }, spaceId);
|
|
36
|
+
server.sendMessage(forbidden);
|
|
47
37
|
await connectionOpen.waitForCount(1);
|
|
48
38
|
|
|
49
39
|
// Double restart to check for race conditions.
|
|
50
40
|
client.setIdentity(await createEphemeralEdgeIdentity());
|
|
51
|
-
sendMessage(
|
|
52
|
-
createForbiddenMessage(
|
|
53
|
-
{
|
|
54
|
-
identityKey: client.identityKey,
|
|
55
|
-
peerKey: client.peerKey,
|
|
56
|
-
},
|
|
57
|
-
spaceId,
|
|
58
|
-
),
|
|
59
|
-
);
|
|
41
|
+
server.sendMessage(forbidden);
|
|
60
42
|
await connectionOpen.waitForCount(1);
|
|
61
43
|
|
|
62
44
|
await replicator.disconnect();
|
|
63
45
|
});
|
|
46
|
+
|
|
47
|
+
describe('shouldAdvertise', () => {
|
|
48
|
+
test('true if space document belongs to connection space', async () => {
|
|
49
|
+
const { client } = await createClientServer();
|
|
50
|
+
|
|
51
|
+
const spaceId = SpaceId.random();
|
|
52
|
+
const documentId = PublicKey.random().toHex();
|
|
53
|
+
const { context, openConnections } = createMockContext({
|
|
54
|
+
documentSpaceId: { [documentId]: spaceId },
|
|
55
|
+
});
|
|
56
|
+
const replicator = await connectReplicator(client, context);
|
|
57
|
+
await replicator.connectToSpace(spaceId);
|
|
58
|
+
|
|
59
|
+
await expect.poll(() => openConnections.length === 1).toBeTruthy();
|
|
60
|
+
expect(openConnections[0].shouldAdvertise({ documentId })).toBeTruthy();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('checks remote collection if space id can not be resolved', async () => {
|
|
64
|
+
const { client } = await createClientServer();
|
|
65
|
+
|
|
66
|
+
const spaceId = SpaceId.random();
|
|
67
|
+
const documentId = PublicKey.random().toHex();
|
|
68
|
+
const remoteCollections: { [peerId: string]: { [documentId: string]: boolean } } = {};
|
|
69
|
+
const { context, openConnections } = createMockContext({ remoteCollections });
|
|
70
|
+
const replicator = await connectReplicator(client, context);
|
|
71
|
+
await replicator.connectToSpace(spaceId);
|
|
72
|
+
|
|
73
|
+
await expect.poll(() => openConnections.length === 1).toBeTruthy();
|
|
74
|
+
const connection = openConnections[0];
|
|
75
|
+
expect(await connection.shouldAdvertise({ documentId })).toBeFalsy();
|
|
76
|
+
remoteCollections[connection.peerId] = { [documentId]: true };
|
|
77
|
+
expect(await connection.shouldAdvertise({ documentId })).toBeTruthy();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const connectReplicator = async (client: EdgeClient, context: EchoReplicatorContext) => {
|
|
82
|
+
const replicator = new EchoEdgeReplicator({ edgeConnection: client });
|
|
83
|
+
await replicator.connect(context);
|
|
84
|
+
onTestFinished(() => replicator.disconnect());
|
|
85
|
+
return replicator;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const createClientServer = async () => {
|
|
89
|
+
const server = await createTestEdgeWsServer(await getRandomPort());
|
|
90
|
+
onTestFinished(server.cleanup);
|
|
91
|
+
const client = new EdgeClient(await createEphemeralEdgeIdentity(), { socketEndpoint: server.endpoint });
|
|
92
|
+
await openAndClose(client);
|
|
93
|
+
return { client, server };
|
|
94
|
+
};
|
|
64
95
|
});
|
|
65
96
|
|
|
66
|
-
const createMockContext = (
|
|
97
|
+
const createMockContext = (args?: {
|
|
98
|
+
remoteCollections?: { [peerId: string]: { [documentId: string]: boolean } };
|
|
99
|
+
documentSpaceId?: { [documentId: string]: SpaceId };
|
|
100
|
+
}) => {
|
|
67
101
|
const connectionOpen = new Event();
|
|
102
|
+
const openConnections: ReplicatorConnection[] = [];
|
|
68
103
|
return {
|
|
69
104
|
context: {
|
|
70
|
-
getContainingSpaceIdForDocument: async (documentId) => null,
|
|
105
|
+
getContainingSpaceIdForDocument: async (documentId) => args?.documentSpaceId?.[documentId] ?? null,
|
|
71
106
|
getContainingSpaceForDocument: async (documentId) => null,
|
|
72
107
|
onConnectionAuthScopeChanged: (connection) => {},
|
|
73
|
-
isDocumentInRemoteCollection: async (params) =>
|
|
74
|
-
|
|
108
|
+
isDocumentInRemoteCollection: async (params) =>
|
|
109
|
+
args?.remoteCollections?.[params.peerId]?.[params.documentId] ?? false,
|
|
110
|
+
onConnectionClosed: (connection) => {
|
|
111
|
+
const idx = openConnections.indexOf(connection);
|
|
112
|
+
if (idx >= 0) {
|
|
113
|
+
openConnections.splice(idx, 1);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
75
116
|
onConnectionOpen: (connection) => {
|
|
117
|
+
openConnections.push(connection);
|
|
76
118
|
connectionOpen.emit();
|
|
77
119
|
},
|
|
78
120
|
|
|
79
121
|
peerId: PublicKey.random().toHex(),
|
|
80
122
|
} satisfies EchoReplicatorContext,
|
|
81
123
|
|
|
124
|
+
openConnections,
|
|
82
125
|
connectionOpen,
|
|
83
126
|
};
|
|
84
127
|
};
|
|
@@ -267,9 +267,20 @@ class EdgeReplicatorConnection extends Resource implements ReplicatorConnection
|
|
|
267
267
|
}
|
|
268
268
|
const spaceId = await this._context.getContainingSpaceIdForDocument(params.documentId);
|
|
269
269
|
if (!spaceId) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
270
|
+
const remoteDocumentExists = await this._context.isDocumentInRemoteCollection({
|
|
271
|
+
documentId: params.documentId,
|
|
272
|
+
peerId: this._remotePeerId as PeerId,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
log.info('document not found locally for share policy check, accepting the remote document', {
|
|
276
|
+
documentId: params.documentId,
|
|
277
|
+
remoteDocumentExists,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// If a document is not present locally return true only if it already exists on edge.
|
|
281
|
+
// Simply returning true will add edge to "generous peers list" for this document which will
|
|
282
|
+
// start replication of the document after we receive it potentially pushing it to replicator of the wrong space.
|
|
283
|
+
return remoteDocumentExists;
|
|
273
284
|
}
|
|
274
285
|
return spaceId === this._spaceId;
|
|
275
286
|
}
|