@dxos/echo-pipeline 0.6.5 → 0.6.6-staging.23d123d
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/{chunk-2MII6KJX.mjs → chunk-P6XSIJKM.mjs} +2184 -2132
- package/dist/lib/browser/chunk-P6XSIJKM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +8 -8
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-6MWU4MHX.cjs → chunk-IYTGTZ7D.cjs} +2177 -2125
- package/dist/lib/node/chunk-IYTGTZ7D.cjs.map +7 -0
- package/dist/lib/node/index.cjs +35 -35
- package/dist/lib/node/index.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +18 -18
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/types/src/automerge/automerge-host.d.ts +1 -0
- package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-network-adapter.d.ts +2 -1
- package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-replicator.d.ts +10 -1
- package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
- package/dist/types/src/automerge/network-protocol.d.ts +3 -28
- package/dist/types/src/automerge/network-protocol.d.ts.map +1 -1
- package/dist/types/src/space/space-manager.d.ts +3 -1
- package/dist/types/src/space/space-manager.d.ts.map +1 -1
- package/dist/types/src/space/space-protocol.d.ts +10 -3
- package/dist/types/src/space/space-protocol.d.ts.map +1 -1
- package/dist/types/src/space/space.d.ts.map +1 -1
- package/dist/types/src/testing/test-network-adapter.d.ts +2 -1
- package/dist/types/src/testing/test-network-adapter.d.ts.map +1 -1
- package/package.json +33 -33
- package/src/automerge/automerge-host.ts +13 -1
- package/src/automerge/automerge-repo.test.ts +380 -366
- package/src/automerge/echo-network-adapter.test.ts +1 -0
- package/src/automerge/echo-network-adapter.ts +8 -0
- package/src/automerge/echo-replicator.ts +11 -1
- package/src/automerge/mesh-echo-replicator-connection.ts +18 -0
- package/src/automerge/mesh-echo-replicator.ts +10 -2
- package/src/automerge/network-protocol.ts +8 -34
- package/src/space/space-manager.ts +15 -2
- package/src/space/space-protocol.ts +42 -5
- package/src/space/space.ts +4 -4
- package/src/testing/test-network-adapter.ts +5 -3
- package/dist/lib/browser/chunk-2MII6KJX.mjs.map +0 -7
- package/dist/lib/node/chunk-6MWU4MHX.cjs.map +0 -7
|
@@ -105,6 +105,7 @@ describe('EchoNetworkAdapter', () => {
|
|
|
105
105
|
const createConnectedAdapter = async (replicator: MeshEchoReplicator) => {
|
|
106
106
|
const adapter = new EchoNetworkAdapter({
|
|
107
107
|
getContainingSpaceForDocument: async () => null,
|
|
108
|
+
isDocumentInRemoteCollection: async () => true,
|
|
108
109
|
onCollectionStateQueried: () => {},
|
|
109
110
|
onCollectionStateReceived: () => {},
|
|
110
111
|
});
|
|
@@ -12,6 +12,7 @@ import { nonNullable } from '@dxos/util';
|
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
14
|
type EchoReplicator,
|
|
15
|
+
type RemoteDocumentExistenceCheckParams,
|
|
15
16
|
type ReplicatorConnection,
|
|
16
17
|
type ShouldAdvertiseParams,
|
|
17
18
|
type ShouldSyncCollectionParams,
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
type CollectionQueryMessage,
|
|
23
24
|
type CollectionStateMessage,
|
|
24
25
|
} from './network-protocol';
|
|
26
|
+
import { createIdFromSpaceKey } from '../space';
|
|
25
27
|
|
|
26
28
|
export interface NetworkDataMonitor {
|
|
27
29
|
recordPeerConnected(peerId: string): void;
|
|
@@ -33,6 +35,7 @@ export interface NetworkDataMonitor {
|
|
|
33
35
|
|
|
34
36
|
export type EchoNetworkAdapterParams = {
|
|
35
37
|
getContainingSpaceForDocument: (documentId: string) => Promise<PublicKey | null>;
|
|
38
|
+
isDocumentInRemoteCollection: (params: RemoteDocumentExistenceCheckParams) => Promise<boolean>;
|
|
36
39
|
onCollectionStateQueried: (collectionId: string, peerId: PeerId) => void;
|
|
37
40
|
onCollectionStateReceived: (collectionId: string, peerId: PeerId, state: unknown) => void;
|
|
38
41
|
monitor?: NetworkDataMonitor;
|
|
@@ -111,7 +114,12 @@ export class EchoNetworkAdapter extends NetworkAdapter {
|
|
|
111
114
|
onConnectionOpen: this._onConnectionOpen.bind(this),
|
|
112
115
|
onConnectionClosed: this._onConnectionClosed.bind(this),
|
|
113
116
|
onConnectionAuthScopeChanged: this._onConnectionAuthScopeChanged.bind(this),
|
|
117
|
+
isDocumentInRemoteCollection: this._params.isDocumentInRemoteCollection,
|
|
114
118
|
getContainingSpaceForDocument: this._params.getContainingSpaceForDocument,
|
|
119
|
+
getContainingSpaceIdForDocument: async (documentId) => {
|
|
120
|
+
const key = await this._params.getContainingSpaceForDocument(documentId);
|
|
121
|
+
return key ? createIdFromSpaceKey(key) : null;
|
|
122
|
+
},
|
|
115
123
|
});
|
|
116
124
|
}
|
|
117
125
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Message } from '@dxos/automerge/automerge-repo';
|
|
6
|
-
import { type PublicKey } from '@dxos/keys';
|
|
6
|
+
import { type PublicKey, type SpaceId } from '@dxos/keys';
|
|
7
7
|
|
|
8
8
|
export interface EchoReplicator {
|
|
9
9
|
/**
|
|
@@ -23,7 +23,12 @@ export interface EchoReplicatorContext {
|
|
|
23
23
|
*/
|
|
24
24
|
get peerId(): string;
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated Use `getContainingSpaceIdForDocument`.
|
|
28
|
+
*/
|
|
26
29
|
getContainingSpaceForDocument(documentId: string): Promise<PublicKey | null>;
|
|
30
|
+
getContainingSpaceIdForDocument(documentId: string): Promise<SpaceId | null>;
|
|
31
|
+
isDocumentInRemoteCollection(params: RemoteDocumentExistenceCheckParams): Promise<boolean>;
|
|
27
32
|
|
|
28
33
|
onConnectionOpen(connection: ReplicatorConnection): void;
|
|
29
34
|
onConnectionClosed(connection: ReplicatorConnection): void;
|
|
@@ -65,3 +70,8 @@ export type ShouldAdvertiseParams = {
|
|
|
65
70
|
export type ShouldSyncCollectionParams = {
|
|
66
71
|
collectionId: string;
|
|
67
72
|
};
|
|
73
|
+
|
|
74
|
+
export type RemoteDocumentExistenceCheckParams = {
|
|
75
|
+
peerId: string;
|
|
76
|
+
documentId: string;
|
|
77
|
+
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import * as A from '@dxos/automerge/automerge';
|
|
5
6
|
import { cbor, type Message } from '@dxos/automerge/automerge-repo';
|
|
6
7
|
import { Resource } from '@dxos/context';
|
|
7
8
|
import { invariant } from '@dxos/invariant';
|
|
@@ -47,6 +48,7 @@ export class MeshReplicatorConnection extends Resource implements ReplicatorConn
|
|
|
47
48
|
write: async (message: Message, controller) => {
|
|
48
49
|
invariant(this._isEnabled, 'Writing to a disabled connection');
|
|
49
50
|
try {
|
|
51
|
+
logSendSync(message);
|
|
50
52
|
await this.replicatorExtension.sendSyncMessage({ payload: cbor.encode(message) });
|
|
51
53
|
} catch (err) {
|
|
52
54
|
controller.error(err);
|
|
@@ -133,3 +135,19 @@ export class MeshReplicatorConnection extends Resource implements ReplicatorConn
|
|
|
133
135
|
this._isEnabled = false;
|
|
134
136
|
}
|
|
135
137
|
}
|
|
138
|
+
|
|
139
|
+
const logSendSync = (message: Message) => {
|
|
140
|
+
log('sendSyncMessage', () => {
|
|
141
|
+
const decodedSyncMessage = message.type === 'sync' && message.data ? A.decodeSyncMessage(message.data) : undefined;
|
|
142
|
+
return {
|
|
143
|
+
sync: decodedSyncMessage && {
|
|
144
|
+
headsLength: decodedSyncMessage.heads.length,
|
|
145
|
+
requesting: decodedSyncMessage.need.length > 0,
|
|
146
|
+
sendingChanges: decodedSyncMessage.changes.length > 0,
|
|
147
|
+
},
|
|
148
|
+
type: message.type,
|
|
149
|
+
from: message.senderId,
|
|
150
|
+
to: message.targetId,
|
|
151
|
+
};
|
|
152
|
+
});
|
|
153
|
+
};
|
|
@@ -83,11 +83,19 @@ export class MeshEchoReplicator implements EchoReplicator {
|
|
|
83
83
|
try {
|
|
84
84
|
const spaceKey = await this._context.getContainingSpaceForDocument(params.documentId);
|
|
85
85
|
if (!spaceKey) {
|
|
86
|
-
|
|
86
|
+
const remoteDocumentExists = await this._context.isDocumentInRemoteCollection({
|
|
87
|
+
documentId: params.documentId,
|
|
88
|
+
peerId: connection.peerId,
|
|
89
|
+
});
|
|
90
|
+
log('document not found locally for share policy check, accepting the remote document', {
|
|
87
91
|
peerId: connection.peerId,
|
|
88
92
|
documentId: params.documentId,
|
|
93
|
+
remoteDocumentExists,
|
|
89
94
|
});
|
|
90
|
-
return
|
|
95
|
+
// If a document is not present locally return true if another peer claims to have it.
|
|
96
|
+
// Simply returning true will add the peer to "generous peers list" for this document which will
|
|
97
|
+
// start replication of the document after we receive, even if the peer is not in the corresponding space.
|
|
98
|
+
return remoteDocumentExists;
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
const spaceId = await createIdFromSpaceKey(spaceKey);
|
|
@@ -2,44 +2,18 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import type { Message
|
|
5
|
+
import type { Message } from '@dxos/automerge/automerge-repo';
|
|
6
|
+
import {
|
|
7
|
+
type CollectionQueryMessage,
|
|
8
|
+
type CollectionStateMessage,
|
|
9
|
+
MESSAGE_TYPE_COLLECTION_QUERY,
|
|
10
|
+
MESSAGE_TYPE_COLLECTION_STATE,
|
|
11
|
+
} from '@dxos/protocols';
|
|
6
12
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
export type CollectionQueryMessage = {
|
|
10
|
-
type: typeof MESSAGE_TYPE_COLLECTION_QUERY;
|
|
11
|
-
senderId: PeerId;
|
|
12
|
-
targetId: PeerId;
|
|
13
|
-
collectionId: string;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Identifier of the current state.
|
|
17
|
-
* Remote peer will skip sending the state if it has the same tag.
|
|
18
|
-
*/
|
|
19
|
-
stateTag?: string;
|
|
20
|
-
};
|
|
13
|
+
export { type CollectionStateMessage, type CollectionQueryMessage };
|
|
21
14
|
|
|
22
15
|
export const isCollectionQueryMessage = (message: Message): message is CollectionQueryMessage =>
|
|
23
16
|
message.type === MESSAGE_TYPE_COLLECTION_QUERY;
|
|
24
17
|
|
|
25
|
-
export const MESSAGE_TYPE_COLLECTION_STATE = 'collection-state';
|
|
26
|
-
|
|
27
|
-
export type CollectionStateMessage = {
|
|
28
|
-
type: typeof MESSAGE_TYPE_COLLECTION_STATE;
|
|
29
|
-
senderId: PeerId;
|
|
30
|
-
targetId: PeerId;
|
|
31
|
-
collectionId: string;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* State representation is implementation-defined.
|
|
35
|
-
*/
|
|
36
|
-
state?: unknown;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Identifier of the current state.
|
|
40
|
-
*/
|
|
41
|
-
stateTag?: string;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
18
|
export const isCollectionStateMessage = (message: Message): message is CollectionStateMessage =>
|
|
45
19
|
message.type === MESSAGE_TYPE_COLLECTION_STATE;
|
|
@@ -34,6 +34,8 @@ export type SpaceManagerParams = {
|
|
|
34
34
|
snapshotStore: SnapshotStore;
|
|
35
35
|
|
|
36
36
|
blobStore: BlobStore;
|
|
37
|
+
|
|
38
|
+
disableP2pReplication?: boolean;
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
export type ConstructSpaceParams = {
|
|
@@ -68,14 +70,23 @@ export class SpaceManager {
|
|
|
68
70
|
private readonly _snapshotStore: SnapshotStore;
|
|
69
71
|
private readonly _blobStore: BlobStore;
|
|
70
72
|
private readonly _instanceId = PublicKey.random().toHex();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
private readonly _disableP2pReplication: boolean;
|
|
74
|
+
|
|
75
|
+
constructor({
|
|
76
|
+
feedStore,
|
|
77
|
+
networkManager,
|
|
78
|
+
metadataStore,
|
|
79
|
+
snapshotStore,
|
|
80
|
+
blobStore,
|
|
81
|
+
disableP2pReplication,
|
|
82
|
+
}: SpaceManagerParams) {
|
|
73
83
|
// TODO(burdon): Assert.
|
|
74
84
|
this._feedStore = feedStore;
|
|
75
85
|
this._networkManager = networkManager;
|
|
76
86
|
this._metadataStore = metadataStore;
|
|
77
87
|
this._snapshotStore = snapshotStore;
|
|
78
88
|
this._blobStore = blobStore;
|
|
89
|
+
this._disableP2pReplication = disableP2pReplication ?? false;
|
|
79
90
|
}
|
|
80
91
|
|
|
81
92
|
// TODO(burdon): Remove.
|
|
@@ -115,6 +126,7 @@ export class SpaceManager {
|
|
|
115
126
|
onSessionAuth: onAuthorizedConnection,
|
|
116
127
|
onAuthFailure,
|
|
117
128
|
blobStore: this._blobStore,
|
|
129
|
+
disableP2pReplication: this._disableP2pReplication,
|
|
118
130
|
});
|
|
119
131
|
const snapshotManager = new SnapshotManager(this._snapshotStore, this._blobStore, protocol.blobSync);
|
|
120
132
|
|
|
@@ -157,6 +169,7 @@ export class SpaceManager {
|
|
|
157
169
|
},
|
|
158
170
|
onAuthFailure: (session: Teleport) => session.close(),
|
|
159
171
|
blobStore: this._blobStore,
|
|
172
|
+
disableP2pReplication: this._disableP2pReplication,
|
|
160
173
|
});
|
|
161
174
|
|
|
162
175
|
try {
|
|
@@ -20,7 +20,7 @@ import { type MuxerStats, Teleport } from '@dxos/teleport';
|
|
|
20
20
|
import { type BlobStore, BlobSync } from '@dxos/teleport-extension-object-sync';
|
|
21
21
|
import { ReplicatorExtension } from '@dxos/teleport-extension-replicator';
|
|
22
22
|
import { trace } from '@dxos/tracing';
|
|
23
|
-
import { ComplexMap } from '@dxos/util';
|
|
23
|
+
import { CallbackCollection, ComplexMap, type AsyncCallback } from '@dxos/util';
|
|
24
24
|
|
|
25
25
|
import { AuthExtension, type AuthProvider, type AuthVerifier } from './auth';
|
|
26
26
|
|
|
@@ -41,12 +41,16 @@ export type SpaceProtocolOptions = {
|
|
|
41
41
|
|
|
42
42
|
blobStore: BlobStore;
|
|
43
43
|
|
|
44
|
+
onFeed?: (feed: FeedWrapper<FeedMessage>) => Promise<void>;
|
|
45
|
+
|
|
44
46
|
/**
|
|
45
47
|
* Called when new session is authenticated.
|
|
46
48
|
* Additional extensions can be added here.
|
|
47
49
|
*/
|
|
48
50
|
onSessionAuth?: (session: Teleport) => void;
|
|
49
51
|
onAuthFailure?: (session: Teleport) => void;
|
|
52
|
+
|
|
53
|
+
disableP2pReplication?: boolean;
|
|
50
54
|
};
|
|
51
55
|
|
|
52
56
|
/**
|
|
@@ -61,6 +65,8 @@ export class SpaceProtocol {
|
|
|
61
65
|
|
|
62
66
|
public readonly blobSync: BlobSync;
|
|
63
67
|
|
|
68
|
+
private readonly _disableP2pReplication: boolean;
|
|
69
|
+
|
|
64
70
|
@logInfo
|
|
65
71
|
@trace.info()
|
|
66
72
|
private readonly _topic: Promise<PublicKey>;
|
|
@@ -79,6 +85,8 @@ export class SpaceProtocol {
|
|
|
79
85
|
|
|
80
86
|
private _connection?: SwarmConnection;
|
|
81
87
|
|
|
88
|
+
public readonly feedAdded = new CallbackCollection<AsyncCallback<FeedWrapper<FeedMessage>>>();
|
|
89
|
+
|
|
82
90
|
get sessions(): ReadonlyMap<PublicKey, SpaceProtocolSession> {
|
|
83
91
|
return this._sessions;
|
|
84
92
|
}
|
|
@@ -92,7 +100,15 @@ export class SpaceProtocol {
|
|
|
92
100
|
return this._swarmIdentity.peerKey;
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
constructor({
|
|
103
|
+
constructor({
|
|
104
|
+
topic,
|
|
105
|
+
swarmIdentity,
|
|
106
|
+
networkManager,
|
|
107
|
+
onSessionAuth,
|
|
108
|
+
onAuthFailure,
|
|
109
|
+
blobStore,
|
|
110
|
+
disableP2pReplication,
|
|
111
|
+
}: SpaceProtocolOptions) {
|
|
96
112
|
this._spaceKey = topic;
|
|
97
113
|
this._networkManager = networkManager;
|
|
98
114
|
this._swarmIdentity = swarmIdentity;
|
|
@@ -102,16 +118,20 @@ export class SpaceProtocol {
|
|
|
102
118
|
|
|
103
119
|
// TODO(burdon): Async race condition? Move to start?
|
|
104
120
|
this._topic = subtleCrypto.digest('SHA-256', topic.asBuffer()).then(discoveryKey).then(PublicKey.from);
|
|
121
|
+
|
|
122
|
+
this._disableP2pReplication = disableP2pReplication ?? false;
|
|
105
123
|
}
|
|
106
124
|
|
|
107
125
|
// TODO(burdon): Create abstraction for Space (e.g., add keys and have provider).
|
|
108
|
-
addFeed(feed: FeedWrapper<FeedMessage>) {
|
|
126
|
+
async addFeed(feed: FeedWrapper<FeedMessage>) {
|
|
109
127
|
log('addFeed', { key: feed.key });
|
|
110
128
|
|
|
111
129
|
this._feeds.add(feed);
|
|
112
130
|
for (const session of this._sessions.values()) {
|
|
113
131
|
session.replicator.addFeed(feed);
|
|
114
132
|
}
|
|
133
|
+
|
|
134
|
+
await this.feedAdded.callSerial(feed);
|
|
115
135
|
}
|
|
116
136
|
|
|
117
137
|
// TODO(burdon): Rename open? Common open/close interfaces for all services?
|
|
@@ -160,6 +180,7 @@ export class SpaceProtocol {
|
|
|
160
180
|
onSessionAuth: this._onSessionAuth,
|
|
161
181
|
onAuthFailure: this._onAuthFailure,
|
|
162
182
|
blobSync: this.blobSync,
|
|
183
|
+
disableP2pReplication: this._disableP2pReplication,
|
|
163
184
|
});
|
|
164
185
|
this._sessions.set(wireParams.remotePeerId, session);
|
|
165
186
|
|
|
@@ -185,6 +206,8 @@ export type SpaceProtocolSessionParams = {
|
|
|
185
206
|
onSessionAuth?: (session: Teleport) => void;
|
|
186
207
|
|
|
187
208
|
onAuthFailure?: (session: Teleport) => void;
|
|
209
|
+
|
|
210
|
+
disableP2pReplication?: boolean;
|
|
188
211
|
};
|
|
189
212
|
|
|
190
213
|
export enum AuthStatus {
|
|
@@ -201,6 +224,8 @@ export class SpaceProtocolSession implements WireProtocol {
|
|
|
201
224
|
@logInfo
|
|
202
225
|
private readonly _wireParams: WireProtocolParams;
|
|
203
226
|
|
|
227
|
+
private readonly _disableP2pReplication: boolean;
|
|
228
|
+
|
|
204
229
|
private readonly _onSessionAuth?: (session: Teleport) => void;
|
|
205
230
|
private readonly _onAuthFailure?: (session: Teleport) => void;
|
|
206
231
|
private readonly _swarmIdentity: SwarmIdentity;
|
|
@@ -223,7 +248,14 @@ export class SpaceProtocolSession implements WireProtocol {
|
|
|
223
248
|
}
|
|
224
249
|
|
|
225
250
|
// TODO(dmaretskyi): Allow to pass in extra extensions.
|
|
226
|
-
constructor({
|
|
251
|
+
constructor({
|
|
252
|
+
wireParams,
|
|
253
|
+
swarmIdentity,
|
|
254
|
+
onSessionAuth,
|
|
255
|
+
onAuthFailure,
|
|
256
|
+
blobSync,
|
|
257
|
+
disableP2pReplication,
|
|
258
|
+
}: SpaceProtocolSessionParams) {
|
|
227
259
|
this._wireParams = wireParams;
|
|
228
260
|
this._swarmIdentity = swarmIdentity;
|
|
229
261
|
this._onSessionAuth = onSessionAuth;
|
|
@@ -231,6 +263,8 @@ export class SpaceProtocolSession implements WireProtocol {
|
|
|
231
263
|
this._blobSync = blobSync;
|
|
232
264
|
|
|
233
265
|
this._teleport = new Teleport(wireParams);
|
|
266
|
+
|
|
267
|
+
this._disableP2pReplication = disableP2pReplication ?? false;
|
|
234
268
|
}
|
|
235
269
|
|
|
236
270
|
get stream() {
|
|
@@ -256,7 +290,10 @@ export class SpaceProtocolSession implements WireProtocol {
|
|
|
256
290
|
},
|
|
257
291
|
}),
|
|
258
292
|
);
|
|
259
|
-
|
|
293
|
+
|
|
294
|
+
if (!this._disableP2pReplication) {
|
|
295
|
+
this._teleport.addExtension('dxos.mesh.teleport.replicator', this.replicator);
|
|
296
|
+
}
|
|
260
297
|
this._teleport.addExtension('dxos.mesh.teleport.blobsync', this._blobSync.createExtension());
|
|
261
298
|
}
|
|
262
299
|
|
package/src/space/space.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Event, synchronized, trackLeaks } from '@dxos/async';
|
|
5
|
+
import { Event, scheduleMicroTask, synchronized, trackLeaks } from '@dxos/async';
|
|
6
6
|
import { type Context, LifecycleState, Resource } from '@dxos/context';
|
|
7
7
|
import { type DelegateInvitationCredential, type FeedInfo, type MemberInfo } from '@dxos/credentials';
|
|
8
8
|
import { subtleCrypto } from '@dxos/crypto';
|
|
@@ -93,8 +93,8 @@ export class Space extends Resource {
|
|
|
93
93
|
const sparse = info.assertion.designation === AdmittedFeed.Designation.DATA;
|
|
94
94
|
|
|
95
95
|
if (!info.key.equals(params.genesisFeed.key)) {
|
|
96
|
-
|
|
97
|
-
this.protocol.addFeed(await params.feedProvider(info.key, { sparse }));
|
|
96
|
+
scheduleMicroTask(this._ctx, async () => {
|
|
97
|
+
await this.protocol.addFeed(await params.feedProvider(info.key, { sparse }));
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
100
|
});
|
|
@@ -119,7 +119,6 @@ export class Space extends Resource {
|
|
|
119
119
|
|
|
120
120
|
// Start replicating the genesis feed.
|
|
121
121
|
this.protocol = params.protocol;
|
|
122
|
-
this.protocol.addFeed(params.genesisFeed);
|
|
123
122
|
}
|
|
124
123
|
|
|
125
124
|
@logInfo
|
|
@@ -192,6 +191,7 @@ export class Space extends Resource {
|
|
|
192
191
|
// Order is important.
|
|
193
192
|
await this._controlPipeline.start();
|
|
194
193
|
await this.protocol.start();
|
|
194
|
+
await this.protocol.addFeed(await this._feedProvider(this._genesisFeedKey));
|
|
195
195
|
|
|
196
196
|
log('opened');
|
|
197
197
|
}
|
|
@@ -7,13 +7,15 @@ import { type Message, NetworkAdapter, type PeerId } from '@dxos/automerge/autom
|
|
|
7
7
|
import { invariant } from '@dxos/invariant';
|
|
8
8
|
import { log } from '@dxos/log';
|
|
9
9
|
|
|
10
|
+
export type TestConnectionStateProvider = () => 'on' | 'off';
|
|
11
|
+
|
|
10
12
|
export class TestAdapter extends NetworkAdapter {
|
|
11
|
-
static createPair() {
|
|
13
|
+
static createPair(connectionStateProvider: TestConnectionStateProvider = () => 'on') {
|
|
12
14
|
const adapter1: TestAdapter = new TestAdapter({
|
|
13
|
-
send: (message: Message) => sleep(10).then(() => adapter2.receive(message)),
|
|
15
|
+
send: (message: Message) => connectionStateProvider() === 'on' && sleep(10).then(() => adapter2.receive(message)),
|
|
14
16
|
});
|
|
15
17
|
const adapter2: TestAdapter = new TestAdapter({
|
|
16
|
-
send: (message: Message) => sleep(10).then(() => adapter1.receive(message)),
|
|
18
|
+
send: (message: Message) => connectionStateProvider() === 'on' && sleep(10).then(() => adapter1.receive(message)),
|
|
17
19
|
});
|
|
18
20
|
|
|
19
21
|
return [adapter1, adapter2];
|