@dxos/echo-pipeline 0.6.1 → 0.6.2-main.8a232a5
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-DMUP426Q.mjs → chunk-UJQ5VS5V.mjs} +383 -196
- package/dist/lib/browser/chunk-UJQ5VS5V.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +59 -562
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +1 -1
- package/dist/lib/node/{chunk-NH5WJKOW.cjs → chunk-RH6TDRML.cjs} +438 -256
- package/dist/lib/node/chunk-RH6TDRML.cjs.map +7 -0
- package/dist/lib/node/index.cjs +80 -581
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +11 -11
- package/dist/types/src/automerge/automerge-host.d.ts +2 -17
- package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-network-adapter.d.ts +1 -1
- package/dist/types/src/automerge/index.d.ts +0 -2
- package/dist/types/src/automerge/index.d.ts.map +1 -1
- package/dist/types/src/db-host/data-service.d.ts +10 -7
- package/dist/types/src/db-host/data-service.d.ts.map +1 -1
- package/dist/types/src/db-host/documents-synchronizer.d.ts +43 -0
- package/dist/types/src/db-host/documents-synchronizer.d.ts.map +1 -0
- package/dist/types/src/db-host/documents-synchronizer.test.d.ts +2 -0
- package/dist/types/src/db-host/documents-synchronizer.test.d.ts.map +1 -0
- package/dist/types/src/db-host/index.d.ts +1 -0
- package/dist/types/src/db-host/index.d.ts.map +1 -1
- package/package.json +33 -33
- package/src/automerge/automerge-host.ts +6 -56
- package/src/automerge/automerge-repo.test.ts +124 -1
- package/src/automerge/echo-network-adapter.ts +1 -1
- package/src/automerge/index.ts +0 -2
- package/src/db-host/data-service.ts +49 -25
- package/src/db-host/documents-synchronizer.test.ts +40 -0
- package/src/db-host/documents-synchronizer.ts +156 -0
- package/src/db-host/index.ts +1 -0
- package/dist/lib/browser/chunk-DMUP426Q.mjs.map +0 -7
- package/dist/lib/node/chunk-NH5WJKOW.cjs.map +0 -7
- package/dist/types/src/automerge/automerge-doc-loader.d.ts +0 -71
- package/dist/types/src/automerge/automerge-doc-loader.d.ts.map +0 -1
- package/dist/types/src/automerge/automerge-doc-loader.test.d.ts +0 -2
- package/dist/types/src/automerge/automerge-doc-loader.test.d.ts.map +0 -1
- package/dist/types/src/automerge/local-host-network-adapter.d.ts +0 -30
- package/dist/types/src/automerge/local-host-network-adapter.d.ts.map +0 -1
- package/src/automerge/automerge-doc-loader.test.ts +0 -103
- package/src/automerge/automerge-doc-loader.ts +0 -267
- package/src/automerge/local-host-network-adapter.ts +0 -115
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { Event } from '@dxos/async';
|
|
6
|
-
import {
|
|
7
|
-
type DocHandle,
|
|
8
|
-
type AutomergeUrl,
|
|
9
|
-
type DocumentId,
|
|
10
|
-
type Repo,
|
|
11
|
-
interpretAsDocumentId,
|
|
12
|
-
} from '@dxos/automerge/automerge-repo';
|
|
13
|
-
import { cancelWithContext, type Context } from '@dxos/context';
|
|
14
|
-
import { warnAfterTimeout } from '@dxos/debug';
|
|
15
|
-
import { type SpaceState, type SpaceDoc, SpaceDocVersion } from '@dxos/echo-protocol';
|
|
16
|
-
import { invariant } from '@dxos/invariant';
|
|
17
|
-
import { type PublicKey, type SpaceId } from '@dxos/keys';
|
|
18
|
-
import { log } from '@dxos/log';
|
|
19
|
-
import { trace } from '@dxos/tracing';
|
|
20
|
-
|
|
21
|
-
type SpaceDocumentLinks = SpaceDoc['links'];
|
|
22
|
-
|
|
23
|
-
export interface AutomergeDocumentLoader {
|
|
24
|
-
onObjectDocumentLoaded: Event<ObjectDocumentLoaded>;
|
|
25
|
-
|
|
26
|
-
getAllHandles(): DocHandle<SpaceDoc>[];
|
|
27
|
-
|
|
28
|
-
loadSpaceRootDocHandle(ctx: Context, spaceState: SpaceState): Promise<void>;
|
|
29
|
-
loadObjectDocument(objectId: string | string[]): void;
|
|
30
|
-
getObjectDocumentId(objectId: string): string | undefined;
|
|
31
|
-
getSpaceRootDocHandle(): DocHandle<SpaceDoc>;
|
|
32
|
-
createDocumentForObject(objectId: string): DocHandle<SpaceDoc>;
|
|
33
|
-
onObjectLinksUpdated(links: SpaceDocumentLinks): void;
|
|
34
|
-
onObjectBoundToDocument(handle: DocHandle<SpaceDoc>, objectId: string): void;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @returns objectIds for which we had document handles or were loading one.
|
|
38
|
-
*/
|
|
39
|
-
clearHandleReferences(): string[];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Manages object <-> docHandle binding and automerge document loading.
|
|
44
|
-
*/
|
|
45
|
-
@trace.resource()
|
|
46
|
-
export class AutomergeDocumentLoaderImpl implements AutomergeDocumentLoader {
|
|
47
|
-
private _spaceRootDocHandle: DocHandle<SpaceDoc> | null = null;
|
|
48
|
-
/**
|
|
49
|
-
* An object id pointer to a handle of the document where the object is stored inline.
|
|
50
|
-
*/
|
|
51
|
-
private readonly _objectDocumentHandles = new Map<string, DocHandle<SpaceDoc>>();
|
|
52
|
-
/**
|
|
53
|
-
* If object was requested via loadObjectDocument but root document links weren't updated yet
|
|
54
|
-
* loading will be triggered in onObjectLinksUpdated callback.
|
|
55
|
-
*/
|
|
56
|
-
private readonly _objectsPendingDocumentLoad = new Set<string>();
|
|
57
|
-
|
|
58
|
-
public readonly onObjectDocumentLoaded = new Event<ObjectDocumentLoaded>();
|
|
59
|
-
|
|
60
|
-
constructor(
|
|
61
|
-
private readonly _spaceId: SpaceId,
|
|
62
|
-
private readonly _repo: Repo,
|
|
63
|
-
/** Legacy Id */
|
|
64
|
-
private readonly _spaceKey: PublicKey,
|
|
65
|
-
) {}
|
|
66
|
-
|
|
67
|
-
getAllHandles(): DocHandle<SpaceDoc>[] {
|
|
68
|
-
return this._spaceRootDocHandle != null
|
|
69
|
-
? [this._spaceRootDocHandle, ...new Set(this._objectDocumentHandles.values())]
|
|
70
|
-
: [];
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
@trace.span({ showInBrowserTimeline: true })
|
|
74
|
-
public async loadSpaceRootDocHandle(ctx: Context, spaceState: SpaceState): Promise<void> {
|
|
75
|
-
if (this._spaceRootDocHandle != null) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
if (!spaceState.rootUrl) {
|
|
79
|
-
throw new Error('Database opened with no rootUrl');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const existingDocHandle = await this._initDocHandle(ctx, spaceState.rootUrl);
|
|
83
|
-
const doc = existingDocHandle.docSync();
|
|
84
|
-
invariant(doc);
|
|
85
|
-
invariant(doc.version === SpaceDocVersion.CURRENT);
|
|
86
|
-
if (doc.access == null) {
|
|
87
|
-
this._initDocAccess(existingDocHandle);
|
|
88
|
-
}
|
|
89
|
-
this._spaceRootDocHandle = existingDocHandle;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public loadObjectDocument(objectIdOrMany: string | string[]) {
|
|
93
|
-
const objectIds = Array.isArray(objectIdOrMany) ? objectIdOrMany : [objectIdOrMany];
|
|
94
|
-
let hasUrlsToLoad = false;
|
|
95
|
-
const urlsToLoad: SpaceDoc['links'] = {};
|
|
96
|
-
for (const objectId of objectIds) {
|
|
97
|
-
invariant(this._spaceRootDocHandle);
|
|
98
|
-
if (this._objectDocumentHandles.has(objectId) || this._objectsPendingDocumentLoad.has(objectId)) {
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
const spaceRootDoc = this._spaceRootDocHandle.docSync();
|
|
102
|
-
invariant(spaceRootDoc);
|
|
103
|
-
const documentUrl = (spaceRootDoc.links ?? {})[objectId];
|
|
104
|
-
if (documentUrl == null) {
|
|
105
|
-
this._objectsPendingDocumentLoad.add(objectId);
|
|
106
|
-
log.info('loading delayed until object links are initialized', { objectId });
|
|
107
|
-
} else {
|
|
108
|
-
urlsToLoad[objectId] = documentUrl;
|
|
109
|
-
hasUrlsToLoad = true;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
if (hasUrlsToLoad) {
|
|
113
|
-
this._loadLinkedObjects(urlsToLoad);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
public getObjectDocumentId(objectId: string): string | undefined {
|
|
118
|
-
invariant(this._spaceRootDocHandle);
|
|
119
|
-
const spaceRootDoc = this._spaceRootDocHandle.docSync();
|
|
120
|
-
invariant(spaceRootDoc);
|
|
121
|
-
if (spaceRootDoc.objects?.[objectId]) {
|
|
122
|
-
return this._spaceRootDocHandle.documentId;
|
|
123
|
-
}
|
|
124
|
-
const documentUrl = (spaceRootDoc.links ?? {})[objectId];
|
|
125
|
-
return documentUrl && interpretAsDocumentId(documentUrl);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
public onObjectLinksUpdated(links: SpaceDocumentLinks) {
|
|
129
|
-
if (!links) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
const linksAwaitingLoad = Object.entries(links).filter(([objectId]) =>
|
|
133
|
-
this._objectsPendingDocumentLoad.has(objectId),
|
|
134
|
-
);
|
|
135
|
-
this._loadLinkedObjects(Object.fromEntries(linksAwaitingLoad));
|
|
136
|
-
linksAwaitingLoad.forEach(([objectId]) => this._objectsPendingDocumentLoad.delete(objectId));
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
public getSpaceRootDocHandle(): DocHandle<SpaceDoc> {
|
|
140
|
-
invariant(this._spaceRootDocHandle);
|
|
141
|
-
return this._spaceRootDocHandle;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
public createDocumentForObject(objectId: string): DocHandle<SpaceDoc> {
|
|
145
|
-
invariant(this._spaceRootDocHandle);
|
|
146
|
-
const spaceDocHandle = this._repo.create<SpaceDoc>({
|
|
147
|
-
version: SpaceDocVersion.CURRENT,
|
|
148
|
-
});
|
|
149
|
-
this._initDocAccess(spaceDocHandle);
|
|
150
|
-
this.onObjectBoundToDocument(spaceDocHandle, objectId);
|
|
151
|
-
this._spaceRootDocHandle.change((newDoc: SpaceDoc) => {
|
|
152
|
-
newDoc.links ??= {};
|
|
153
|
-
newDoc.links[objectId] = spaceDocHandle.url;
|
|
154
|
-
});
|
|
155
|
-
return spaceDocHandle;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
public onObjectBoundToDocument(handle: DocHandle<SpaceDoc>, objectId: string) {
|
|
159
|
-
this._objectDocumentHandles.set(objectId, handle);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
public clearHandleReferences(): string[] {
|
|
163
|
-
const objectsWithHandles = [...this._objectDocumentHandles.keys()];
|
|
164
|
-
this._objectDocumentHandles.clear();
|
|
165
|
-
this._spaceRootDocHandle = null;
|
|
166
|
-
return objectsWithHandles;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
private _loadLinkedObjects(links: SpaceDocumentLinks) {
|
|
170
|
-
if (!links) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
for (const [objectId, automergeUrl] of Object.entries(links)) {
|
|
174
|
-
const logMeta = { objectId, automergeUrl };
|
|
175
|
-
const objectDocumentHandle = this._objectDocumentHandles.get(objectId);
|
|
176
|
-
if (objectDocumentHandle != null && objectDocumentHandle.url !== automergeUrl) {
|
|
177
|
-
log.warn('object already inlined in a different document, ignoring the link', {
|
|
178
|
-
...logMeta,
|
|
179
|
-
actualDocumentUrl: objectDocumentHandle.url,
|
|
180
|
-
});
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
if (objectDocumentHandle?.url === automergeUrl) {
|
|
184
|
-
log.warn('object document was already loaded', logMeta);
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
const handle = this._repo.find<SpaceDoc>(automergeUrl as DocumentId);
|
|
188
|
-
log.debug('document loading triggered', logMeta);
|
|
189
|
-
this._objectDocumentHandles.set(objectId, handle);
|
|
190
|
-
void this._createObjectOnDocumentLoad(handle, objectId);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
private async _initDocHandle(ctx: Context, url: string) {
|
|
195
|
-
const docHandle = this._repo.find<SpaceDoc>(url as DocumentId);
|
|
196
|
-
while (true) {
|
|
197
|
-
try {
|
|
198
|
-
await warnAfterTimeout(5_000, 'Automerge root doc load timeout (CoreDatabase)', async () => {
|
|
199
|
-
await cancelWithContext(ctx, docHandle.whenReady()); // TODO(dmaretskyi): Temporary 5s timeout for debugging.
|
|
200
|
-
});
|
|
201
|
-
break;
|
|
202
|
-
} catch (err) {
|
|
203
|
-
if (`${err}`.includes('Timeout')) {
|
|
204
|
-
log.info('wraparound', { id: docHandle.documentId, state: docHandle.state });
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
throw err;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (docHandle.state === 'unavailable') {
|
|
213
|
-
throw new Error('Automerge document is unavailable');
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return docHandle;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
private _initDocAccess(handle: DocHandle<SpaceDoc>) {
|
|
220
|
-
handle.change((newDoc: SpaceDoc) => {
|
|
221
|
-
newDoc.access ??= { spaceKey: this._spaceKey.toHex() };
|
|
222
|
-
newDoc.access.spaceKey = this._spaceKey.toHex();
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
private async _createObjectOnDocumentLoad(handle: DocHandle<SpaceDoc>, objectId: string) {
|
|
227
|
-
try {
|
|
228
|
-
await handle.whenReady();
|
|
229
|
-
const logMeta = { objectId, docUrl: handle.url };
|
|
230
|
-
if (this.onObjectDocumentLoaded.listenerCount() === 0) {
|
|
231
|
-
log.info('document loaded after all listeners were removed', logMeta);
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
const objectDocHandle = this._objectDocumentHandles.get(objectId);
|
|
235
|
-
if (objectDocHandle?.url !== handle.url) {
|
|
236
|
-
log.warn('object was rebound while a document was loading, discarding handle', logMeta);
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
this.onObjectDocumentLoaded.emit({ handle, objectId });
|
|
240
|
-
} catch (err) {
|
|
241
|
-
const shouldRetryLoading = this.onObjectDocumentLoaded.listenerCount() > 0;
|
|
242
|
-
log.warn('failed to load a document', {
|
|
243
|
-
objectId,
|
|
244
|
-
automergeUrl: handle.url,
|
|
245
|
-
retryLoading: shouldRetryLoading,
|
|
246
|
-
err,
|
|
247
|
-
});
|
|
248
|
-
if (shouldRetryLoading) {
|
|
249
|
-
await this._createObjectOnDocumentLoad(handle, objectId);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
export interface ObjectDocumentLoaded {
|
|
256
|
-
handle: DocHandle<SpaceDoc>;
|
|
257
|
-
objectId: string;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
export interface DocumentChanges {
|
|
261
|
-
createdObjectIds: string[];
|
|
262
|
-
updatedObjectIds: string[];
|
|
263
|
-
objectsToRebind: string[];
|
|
264
|
-
linkedDocuments: {
|
|
265
|
-
[echoId: string]: AutomergeUrl;
|
|
266
|
-
};
|
|
267
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { Trigger } from '@dxos/async';
|
|
6
|
-
import { NetworkAdapter, type Message, type PeerId, cbor } from '@dxos/automerge/automerge-repo';
|
|
7
|
-
import { Stream } from '@dxos/codec-protobuf';
|
|
8
|
-
import { invariant } from '@dxos/invariant';
|
|
9
|
-
import { type HostInfo, type SyncRepoRequest, type SyncRepoResponse } from '@dxos/protocols/proto/dxos/echo/service';
|
|
10
|
-
|
|
11
|
-
type ClientSyncState = {
|
|
12
|
-
connected: boolean;
|
|
13
|
-
send: (message: Message) => void;
|
|
14
|
-
disconnect: () => void;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Used to replicate with apps running on the same device.
|
|
19
|
-
*/
|
|
20
|
-
export class LocalHostNetworkAdapter extends NetworkAdapter {
|
|
21
|
-
private readonly _peers: Map<PeerId, ClientSyncState> = new Map();
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Emits `ready` event. That signals to `Repo` that it can start using the adapter.
|
|
25
|
-
*/
|
|
26
|
-
ready() {
|
|
27
|
-
// NOTE: Emitting `ready` event in NetworkAdapter`s constructor causes a race condition
|
|
28
|
-
// because `Repo` waits for `ready` event (which it never receives) before it starts using the adapter.
|
|
29
|
-
this.emit('ready', {
|
|
30
|
-
network: this,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
private readonly _connected = new Trigger();
|
|
35
|
-
private _isConnected: boolean = false;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Called by `Repo` to connect to the network.
|
|
39
|
-
*
|
|
40
|
-
* @param peerId Our peer Id.
|
|
41
|
-
*/
|
|
42
|
-
override connect(peerId: PeerId): void {
|
|
43
|
-
this.peerId = peerId;
|
|
44
|
-
this._isConnected = true;
|
|
45
|
-
this._connected.wake();
|
|
46
|
-
// No-op. Client always connects first
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
override send(message: Message): void {
|
|
50
|
-
const peer = this._peers.get(message.targetId);
|
|
51
|
-
invariant(peer, 'Peer not found.');
|
|
52
|
-
peer.send(message);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async close() {
|
|
56
|
-
this._peers.forEach((peer) => peer.disconnect());
|
|
57
|
-
this.emit('close');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
override disconnect(): void {
|
|
61
|
-
// TODO(mykola): `disconnect` is not used anywhere in `Repo` from `@automerge/automerge-repo`. Should we remove it?
|
|
62
|
-
// No-op
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async whenConnected(): Promise<void> {
|
|
66
|
-
await this._connected.wait({ timeout: 10_000 });
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
syncRepo({ id, syncMessage }: SyncRepoRequest): Stream<SyncRepoResponse> {
|
|
70
|
-
const peerId = this._getPeerId(id);
|
|
71
|
-
|
|
72
|
-
return new Stream(({ next, close }) => {
|
|
73
|
-
invariant(!this._peers.has(peerId), 'Peer already connected.');
|
|
74
|
-
this._peers.set(peerId, {
|
|
75
|
-
connected: true,
|
|
76
|
-
send: (message) => {
|
|
77
|
-
next({
|
|
78
|
-
syncMessage: cbor.encode(message),
|
|
79
|
-
});
|
|
80
|
-
},
|
|
81
|
-
disconnect: () => {
|
|
82
|
-
this._peers.delete(peerId);
|
|
83
|
-
close();
|
|
84
|
-
this.emit('peer-disconnected', {
|
|
85
|
-
peerId,
|
|
86
|
-
});
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
invariant(this._isConnected);
|
|
91
|
-
this.emit('peer-candidate', {
|
|
92
|
-
peerMetadata: {},
|
|
93
|
-
peerId,
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async sendSyncMessage({ id, syncMessage }: SyncRepoRequest): Promise<void> {
|
|
99
|
-
invariant(this._isConnected);
|
|
100
|
-
const message = cbor.decode(syncMessage!) as Message;
|
|
101
|
-
this.emit('message', message);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async getHostInfo(): Promise<HostInfo> {
|
|
105
|
-
invariant(this._isConnected);
|
|
106
|
-
invariant(this.peerId, 'Peer id not set.');
|
|
107
|
-
return {
|
|
108
|
-
peerId: this.peerId,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
private _getPeerId(id: string): PeerId {
|
|
113
|
-
return id as PeerId;
|
|
114
|
-
}
|
|
115
|
-
}
|