@dxos/echo-pipeline 0.5.9-main.a2de4fa → 0.5.9-main.a75fa71
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-I2J5TTHJ.mjs → chunk-HS77A4I4.mjs} +174 -23
- package/dist/lib/browser/{chunk-I2J5TTHJ.mjs.map → chunk-HS77A4I4.mjs.map} +4 -4
- package/dist/lib/browser/index.mjs +91 -42
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +11 -7
- package/dist/lib/browser/testing/index.mjs.map +1 -1
- package/dist/lib/node/{chunk-QPCNQ4ZK.cjs → chunk-Y5U7UXEL.cjs} +185 -34
- package/dist/lib/node/{chunk-QPCNQ4ZK.cjs.map → chunk-Y5U7UXEL.cjs.map} +4 -4
- package/dist/lib/node/index.cjs +126 -77
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +21 -17
- package/dist/lib/node/testing/index.cjs.map +1 -1
- package/dist/types/src/automerge/automerge-host.d.ts +40 -2
- package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-network-adapter.d.ts +2 -0
- package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
- package/dist/types/src/space/admission-discovery-extension.d.ts +30 -0
- package/dist/types/src/space/admission-discovery-extension.d.ts.map +1 -0
- package/dist/types/src/space/index.d.ts +1 -0
- package/dist/types/src/space/index.d.ts.map +1 -1
- package/dist/types/src/space/space-manager.d.ts +8 -0
- package/dist/types/src/space/space-manager.d.ts.map +1 -1
- package/package.json +33 -33
- package/src/automerge/automerge-host.ts +96 -18
- package/src/automerge/echo-network-adapter.ts +10 -4
- package/src/automerge/mesh-echo-replicator.ts +1 -1
- package/src/space/admission-discovery-extension.ts +90 -0
- package/src/space/index.ts +1 -0
- package/src/space/space-manager.ts +46 -1
|
@@ -4,6 +4,7 @@ import { PublicKey } from '@dxos/keys';
|
|
|
4
4
|
import { type SwarmNetworkManager } from '@dxos/network-manager';
|
|
5
5
|
import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
6
6
|
import { type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
7
|
+
import type { Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
7
8
|
import { type Teleport } from '@dxos/teleport';
|
|
8
9
|
import { type BlobStore } from '@dxos/teleport-extension-object-sync';
|
|
9
10
|
import { ComplexMap } from '@dxos/util';
|
|
@@ -33,6 +34,12 @@ export type ConstructSpaceParams = {
|
|
|
33
34
|
onDelegatedInvitationStatusChange: (invitation: DelegateInvitationCredential, isActive: boolean) => Promise<void>;
|
|
34
35
|
onMemberRolesChanged: (member: MemberInfo[]) => Promise<void>;
|
|
35
36
|
};
|
|
37
|
+
export type RequestSpaceAdmissionCredentialParams = {
|
|
38
|
+
spaceKey: PublicKey;
|
|
39
|
+
identityKey: PublicKey;
|
|
40
|
+
swarmIdentity: SwarmIdentity;
|
|
41
|
+
timeout: number;
|
|
42
|
+
};
|
|
36
43
|
/**
|
|
37
44
|
* Manages a collection of ECHO (Data) Spaces.
|
|
38
45
|
*/
|
|
@@ -49,5 +56,6 @@ export declare class SpaceManager {
|
|
|
49
56
|
open(): Promise<void>;
|
|
50
57
|
close(): Promise<void>;
|
|
51
58
|
constructSpace({ metadata, swarmIdentity, onAuthorizedConnection, onAuthFailure, onDelegatedInvitationStatusChange, onMemberRolesChanged, memberKey, }: ConstructSpaceParams): Promise<Space>;
|
|
59
|
+
requestSpaceAdmissionCredential(params: RequestSpaceAdmissionCredentialParams): Promise<Credential>;
|
|
52
60
|
}
|
|
53
61
|
//# sourceMappingURL=space-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"space-manager.d.ts","sourceRoot":"","sources":["../../../../src/space/space-manager.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,4BAA4B,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEvF,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,sCAAsC,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"space-manager.d.ts","sourceRoot":"","sources":["../../../../src/space/space-manager.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,4BAA4B,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEvF,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,sCAAsC,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGxC,OAAO,EAAE,KAAK,EAAwB,MAAM,SAAS,CAAC;AACtD,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;IAClC,cAAc,EAAE,mBAAmB,CAAC;IACpC,aAAa,EAAE,aAAa,CAAC;IAE7B;;OAEG;IACH,aAAa,EAAE,aAAa,CAAC;IAE7B,SAAS,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,aAAa,CAAC;IACxB,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,EAAE,SAAS,CAAC;IACrB;;OAEG;IACH,sBAAsB,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpD,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC5C,iCAAiC,EAAE,CAAC,UAAU,EAAE,4BAA4B,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClH,oBAAoB,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG;IAClD,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,EAAE,SAAS,CAAC;IACvB,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,qBACa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoD;IAC5E,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IACpD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAsB;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA8B;gBAE9C,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,EAAE,kBAAkB;IAUtG,IAAI,MAAM,iCAET;IAGK,IAAI;IAGJ,KAAK;IAIL,cAAc,CAAC,EACnB,QAAQ,EACR,aAAa,EACb,sBAAsB,EACtB,aAAa,EACb,iCAAiC,EACjC,oBAAoB,EACpB,SAAS,GACV,EAAE,oBAAoB;IAqCV,+BAA+B,CAAC,MAAM,EAAE,qCAAqC,GAAG,OAAO,CAAC,UAAU,CAAC;CAmCjH"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-pipeline",
|
|
3
|
-
"version": "0.5.9-main.
|
|
3
|
+
"version": "0.5.9-main.a75fa71",
|
|
4
4
|
"description": "ECHO database.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -39,38 +39,38 @@
|
|
|
39
39
|
"crc-32": "^1.2.2",
|
|
40
40
|
"level": "^8.0.1",
|
|
41
41
|
"level-transcoder": "^1.0.1",
|
|
42
|
-
"@dxos/async": "0.5.9-main.
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/crypto": "0.5.9-main.
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/
|
|
51
|
-
"@dxos/
|
|
52
|
-
"@dxos/
|
|
53
|
-
"@dxos/
|
|
54
|
-
"@dxos/
|
|
55
|
-
"@dxos/
|
|
56
|
-
"@dxos/
|
|
57
|
-
"@dxos/kv-store": "0.5.9-main.
|
|
58
|
-
"@dxos/log": "0.5.9-main.
|
|
59
|
-
"@dxos/messaging": "0.5.9-main.
|
|
60
|
-
"@dxos/network-manager": "0.5.9-main.
|
|
61
|
-
"@dxos/node-std": "0.5.9-main.
|
|
62
|
-
"@dxos/
|
|
63
|
-
"@dxos/
|
|
64
|
-
"@dxos/
|
|
65
|
-
"@dxos/
|
|
66
|
-
"@dxos/teleport-extension-
|
|
67
|
-
"@dxos/teleport-extension-
|
|
68
|
-
"@dxos/teleport-extension-object-sync": "0.5.9-main.
|
|
69
|
-
"@dxos/timeframe": "0.5.9-main.
|
|
70
|
-
"@dxos/teleport-extension-replicator": "0.5.9-main.
|
|
71
|
-
"@dxos/tracing": "0.5.9-main.
|
|
72
|
-
"@dxos/typings": "0.5.9-main.
|
|
73
|
-
"@dxos/util": "0.5.9-main.
|
|
42
|
+
"@dxos/async": "0.5.9-main.a75fa71",
|
|
43
|
+
"@dxos/context": "0.5.9-main.a75fa71",
|
|
44
|
+
"@dxos/automerge": "0.5.9-main.a75fa71",
|
|
45
|
+
"@dxos/credentials": "0.5.9-main.a75fa71",
|
|
46
|
+
"@dxos/codec-protobuf": "0.5.9-main.a75fa71",
|
|
47
|
+
"@dxos/crypto": "0.5.9-main.a75fa71",
|
|
48
|
+
"@dxos/echo-schema": "0.5.9-main.a75fa71",
|
|
49
|
+
"@dxos/debug": "0.5.9-main.a75fa71",
|
|
50
|
+
"@dxos/feed-store": "0.5.9-main.a75fa71",
|
|
51
|
+
"@dxos/echo-protocol": "0.5.9-main.a75fa71",
|
|
52
|
+
"@dxos/hypercore": "0.5.9-main.a75fa71",
|
|
53
|
+
"@dxos/indexing": "0.5.9-main.a75fa71",
|
|
54
|
+
"@dxos/keyring": "0.5.9-main.a75fa71",
|
|
55
|
+
"@dxos/keys": "0.5.9-main.a75fa71",
|
|
56
|
+
"@dxos/invariant": "0.5.9-main.a75fa71",
|
|
57
|
+
"@dxos/kv-store": "0.5.9-main.a75fa71",
|
|
58
|
+
"@dxos/log": "0.5.9-main.a75fa71",
|
|
59
|
+
"@dxos/messaging": "0.5.9-main.a75fa71",
|
|
60
|
+
"@dxos/network-manager": "0.5.9-main.a75fa71",
|
|
61
|
+
"@dxos/node-std": "0.5.9-main.a75fa71",
|
|
62
|
+
"@dxos/random-access-storage": "0.5.9-main.a75fa71",
|
|
63
|
+
"@dxos/protocols": "0.5.9-main.a75fa71",
|
|
64
|
+
"@dxos/rpc": "0.5.9-main.a75fa71",
|
|
65
|
+
"@dxos/teleport": "0.5.9-main.a75fa71",
|
|
66
|
+
"@dxos/teleport-extension-automerge-replicator": "0.5.9-main.a75fa71",
|
|
67
|
+
"@dxos/teleport-extension-gossip": "0.5.9-main.a75fa71",
|
|
68
|
+
"@dxos/teleport-extension-object-sync": "0.5.9-main.a75fa71",
|
|
69
|
+
"@dxos/timeframe": "0.5.9-main.a75fa71",
|
|
70
|
+
"@dxos/teleport-extension-replicator": "0.5.9-main.a75fa71",
|
|
71
|
+
"@dxos/tracing": "0.5.9-main.a75fa71",
|
|
72
|
+
"@dxos/typings": "0.5.9-main.a75fa71",
|
|
73
|
+
"@dxos/util": "0.5.9-main.a75fa71"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"fast-check": "^3.19.0",
|
|
@@ -2,10 +2,19 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Event } from '@dxos/async';
|
|
6
|
-
import {
|
|
5
|
+
import { Event, asyncTimeout } from '@dxos/async';
|
|
6
|
+
import {
|
|
7
|
+
next as automerge,
|
|
8
|
+
getBackend,
|
|
9
|
+
getHeads,
|
|
10
|
+
isAutomerge,
|
|
11
|
+
save,
|
|
12
|
+
type Doc,
|
|
13
|
+
type Heads,
|
|
14
|
+
} from '@dxos/automerge/automerge';
|
|
7
15
|
import {
|
|
8
16
|
Repo,
|
|
17
|
+
type AnyDocumentId,
|
|
9
18
|
type DocHandle,
|
|
10
19
|
type DocHandleChangePayload,
|
|
11
20
|
type DocumentId,
|
|
@@ -13,10 +22,9 @@ import {
|
|
|
13
22
|
type StorageAdapterInterface,
|
|
14
23
|
} from '@dxos/automerge/automerge-repo';
|
|
15
24
|
import { type Stream } from '@dxos/codec-protobuf';
|
|
16
|
-
import { Context, type Lifecycle } from '@dxos/context';
|
|
25
|
+
import { Context, cancelWithContext, type Lifecycle } from '@dxos/context';
|
|
17
26
|
import { type SpaceDoc } from '@dxos/echo-protocol';
|
|
18
27
|
import { type IndexMetadataStore } from '@dxos/indexing';
|
|
19
|
-
import { invariant } from '@dxos/invariant';
|
|
20
28
|
import { PublicKey } from '@dxos/keys';
|
|
21
29
|
import { type SublevelDB } from '@dxos/kv-store';
|
|
22
30
|
import { objectPointerCodec } from '@dxos/protocols';
|
|
@@ -29,7 +37,7 @@ import {
|
|
|
29
37
|
import { trace } from '@dxos/tracing';
|
|
30
38
|
import { mapValues } from '@dxos/util';
|
|
31
39
|
|
|
32
|
-
import { EchoNetworkAdapter } from './echo-network-adapter';
|
|
40
|
+
import { EchoNetworkAdapter, isEchoPeerMetadata } from './echo-network-adapter';
|
|
33
41
|
import { type EchoReplicator } from './echo-replicator';
|
|
34
42
|
import { LevelDBStorageAdapter, type BeforeSaveParams } from './leveldb-storage-adapter';
|
|
35
43
|
import { LocalHostNetworkAdapter } from './local-host-network-adapter';
|
|
@@ -43,6 +51,20 @@ export type AutomergeHostParams = {
|
|
|
43
51
|
indexMetadataStore: IndexMetadataStore;
|
|
44
52
|
};
|
|
45
53
|
|
|
54
|
+
export type LoadDocOptions = {
|
|
55
|
+
timeout?: number;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type CreateDocOptions = {
|
|
59
|
+
/**
|
|
60
|
+
* Import the document together with its history.
|
|
61
|
+
*/
|
|
62
|
+
preserveHistory?: boolean;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Abstracts over the AutomergeRepo.
|
|
67
|
+
*/
|
|
46
68
|
@trace.resource()
|
|
47
69
|
export class AutomergeHost {
|
|
48
70
|
private readonly _indexMetadataStore: IndexMetadataStore;
|
|
@@ -102,6 +124,9 @@ export class AutomergeHost {
|
|
|
102
124
|
await this._ctx.dispose();
|
|
103
125
|
}
|
|
104
126
|
|
|
127
|
+
/**
|
|
128
|
+
* @deprecated To be abstracted away.
|
|
129
|
+
*/
|
|
105
130
|
get repo(): Repo {
|
|
106
131
|
return this._repo;
|
|
107
132
|
}
|
|
@@ -114,6 +139,46 @@ export class AutomergeHost {
|
|
|
114
139
|
await this._echoNetworkAdapter.removeReplicator(replicator);
|
|
115
140
|
}
|
|
116
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Loads the document handle from the repo and waits for it to be ready.
|
|
144
|
+
*/
|
|
145
|
+
async loadDoc<T>(ctx: Context, documentId: AnyDocumentId, opts?: LoadDocOptions): Promise<DocHandle<T>> {
|
|
146
|
+
let handle: DocHandle<T> | undefined;
|
|
147
|
+
if (typeof documentId === 'string') {
|
|
148
|
+
// NOTE: documentId might also be a URL, in which case this lookup will fail.
|
|
149
|
+
handle = this._repo.handles[documentId as DocumentId];
|
|
150
|
+
}
|
|
151
|
+
if (!handle) {
|
|
152
|
+
handle = this._repo.find(documentId as DocumentId);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// `whenReady` creates a timeout so we guard it with an if to skip it if the handle is already ready.
|
|
156
|
+
if (!handle.isReady()) {
|
|
157
|
+
if (!opts?.timeout) {
|
|
158
|
+
await cancelWithContext(ctx, handle.whenReady());
|
|
159
|
+
} else {
|
|
160
|
+
await cancelWithContext(ctx, asyncTimeout(handle.whenReady(), opts.timeout));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return handle;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Create new persisted document.
|
|
169
|
+
*/
|
|
170
|
+
createDoc<T>(initialValue?: T | Doc<T>, opts?: CreateDocOptions): DocHandle<T> {
|
|
171
|
+
if (opts?.preserveHistory) {
|
|
172
|
+
if (!isAutomerge(initialValue)) {
|
|
173
|
+
throw new TypeError('Initial value must be an Automerge document');
|
|
174
|
+
}
|
|
175
|
+
// TODO(dmaretskyi): There's a more efficient way.
|
|
176
|
+
return this._repo.import(save(initialValue as Doc<T>));
|
|
177
|
+
} else {
|
|
178
|
+
return this._repo.create(initialValue);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
117
182
|
// TODO(dmaretskyi): Share based on HALO permissions and space affinity.
|
|
118
183
|
// Hosts, running in the worker, don't share documents unless requested by other peers.
|
|
119
184
|
// NOTE: If both peers return sharePolicy=false the replication will not happen
|
|
@@ -131,7 +196,7 @@ export class AutomergeHost {
|
|
|
131
196
|
}
|
|
132
197
|
|
|
133
198
|
const peerMetadata = this.repo.peerMetadataByPeerId[peerId];
|
|
134
|
-
if ((peerMetadata
|
|
199
|
+
if (isEchoPeerMetadata(peerMetadata)) {
|
|
135
200
|
return this._echoNetworkAdapter.shouldAdvertize(peerId, { documentId });
|
|
136
201
|
}
|
|
137
202
|
|
|
@@ -212,39 +277,52 @@ export class AutomergeHost {
|
|
|
212
277
|
return PublicKey.from(spaceKeyHex);
|
|
213
278
|
}
|
|
214
279
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
280
|
+
/**
|
|
281
|
+
* Flush documents to disk.
|
|
282
|
+
*/
|
|
218
283
|
@trace.span({ showInBrowserTimeline: true })
|
|
219
284
|
async flush({ states }: FlushRequest): Promise<void> {
|
|
220
285
|
// Note: Wait for all requested documents to be loaded/synced from thin-client.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
286
|
+
if (states) {
|
|
287
|
+
await Promise.all(
|
|
288
|
+
states.map(async ({ heads, documentId }) => {
|
|
289
|
+
if (!heads) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const handle = this.repo.handles[documentId as DocumentId] ?? this._repo.find(documentId as DocumentId);
|
|
293
|
+
await waitForHeads(handle, heads);
|
|
294
|
+
}) ?? [],
|
|
295
|
+
);
|
|
296
|
+
}
|
|
228
297
|
|
|
229
298
|
await this._repo.flush(states?.map(({ documentId }) => documentId as DocumentId));
|
|
230
299
|
}
|
|
231
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Host <-> Client sync.
|
|
303
|
+
*/
|
|
232
304
|
syncRepo(request: SyncRepoRequest): Stream<SyncRepoResponse> {
|
|
233
305
|
return this._clientNetwork.syncRepo(request);
|
|
234
306
|
}
|
|
235
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Host <-> Client sync.
|
|
310
|
+
*/
|
|
236
311
|
sendSyncMessage(request: SyncRepoRequest): Promise<void> {
|
|
237
312
|
return this._clientNetwork.sendSyncMessage(request);
|
|
238
313
|
}
|
|
239
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Host <-> Client sync.
|
|
317
|
+
*/
|
|
240
318
|
async getHostInfo(): Promise<HostInfo> {
|
|
241
319
|
return this._clientNetwork.getHostInfo();
|
|
242
320
|
}
|
|
243
321
|
}
|
|
244
322
|
|
|
245
|
-
export const getSpaceKeyFromDoc = (doc:
|
|
323
|
+
export const getSpaceKeyFromDoc = (doc: Doc<SpaceDoc>): string | null => {
|
|
246
324
|
// experimental_spaceKey is set on old documents, new ones are created with doc.access.spaceKey
|
|
247
|
-
const rawSpaceKey = doc.access?.spaceKey ?? doc.experimental_spaceKey;
|
|
325
|
+
const rawSpaceKey = doc.access?.spaceKey ?? (doc as any).experimental_spaceKey;
|
|
248
326
|
if (rawSpaceKey == null) {
|
|
249
327
|
return null;
|
|
250
328
|
}
|
|
@@ -174,10 +174,7 @@ export class EchoNetworkAdapter extends NetworkAdapter {
|
|
|
174
174
|
private _emitPeerCandidate(connection: ReplicatorConnection) {
|
|
175
175
|
this.emit('peer-candidate', {
|
|
176
176
|
peerId: connection.peerId as PeerId,
|
|
177
|
-
peerMetadata:
|
|
178
|
-
// TODO(dmaretskyi): Refactor this.
|
|
179
|
-
dxos_peerSource: 'EchoNetworkAdapter',
|
|
180
|
-
} as any,
|
|
177
|
+
peerMetadata: createEchoPeerMetadata(),
|
|
181
178
|
});
|
|
182
179
|
}
|
|
183
180
|
}
|
|
@@ -188,3 +185,12 @@ type ConnectionEntry = {
|
|
|
188
185
|
writer: WritableStreamDefaultWriter<Message>;
|
|
189
186
|
isOpen: boolean;
|
|
190
187
|
};
|
|
188
|
+
|
|
189
|
+
export const createEchoPeerMetadata = (): PeerMetadata =>
|
|
190
|
+
({
|
|
191
|
+
// TODO(dmaretskyi): Refactor this.
|
|
192
|
+
dxos_peerSource: 'EchoNetworkAdapter',
|
|
193
|
+
}) as any;
|
|
194
|
+
|
|
195
|
+
export const isEchoPeerMetadata = (metadata: PeerMetadata): boolean =>
|
|
196
|
+
(metadata as any)?.dxos_peerSource === 'EchoNetworkAdapter';
|
|
@@ -70,8 +70,8 @@ export class MeshEchoReplicator implements EchoReplicator {
|
|
|
70
70
|
onRemoteDisconnected: async () => {
|
|
71
71
|
log('onRemoteDisconnected', { peerId: connection.peerId });
|
|
72
72
|
this._context?.onConnectionClosed(connection);
|
|
73
|
-
await connection.disable();
|
|
74
73
|
this._connectionsPerPeer.delete(connection.peerId);
|
|
74
|
+
await connection.disable();
|
|
75
75
|
this._connections.delete(connection);
|
|
76
76
|
},
|
|
77
77
|
shouldAdvertize: async (params: ShouldAdvertizeParams) => {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { scheduleTask, type Trigger } from '@dxos/async';
|
|
6
|
+
import { Context } from '@dxos/context';
|
|
7
|
+
import { ProtocolError, schema } from '@dxos/protocols';
|
|
8
|
+
import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
9
|
+
import {
|
|
10
|
+
type AdmissionDiscoveryService,
|
|
11
|
+
type GetAdmissionCredentialResponse,
|
|
12
|
+
type GetAdmissionCredentialRequest,
|
|
13
|
+
} from '@dxos/protocols/proto/dxos/mesh/teleport';
|
|
14
|
+
import { type ExtensionContext, RpcExtension } from '@dxos/teleport';
|
|
15
|
+
|
|
16
|
+
import { type Space } from './space';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Guest's side for a connection to a concrete peer in p2p network during invitation.
|
|
20
|
+
*/
|
|
21
|
+
export class CredentialRetrieverExtension extends RpcExtension<
|
|
22
|
+
{ AdmissionDiscoveryService: AdmissionDiscoveryService },
|
|
23
|
+
{}
|
|
24
|
+
> {
|
|
25
|
+
private _ctx = new Context();
|
|
26
|
+
|
|
27
|
+
constructor(
|
|
28
|
+
private readonly _request: GetAdmissionCredentialRequest,
|
|
29
|
+
private readonly _onResult: Trigger<Credential>,
|
|
30
|
+
) {
|
|
31
|
+
super({
|
|
32
|
+
requested: {
|
|
33
|
+
AdmissionDiscoveryService: schema.getService('dxos.mesh.teleport.AdmissionDiscoveryService'),
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
protected override async getHandlers(): Promise<{}> {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
override async onOpen(context: ExtensionContext) {
|
|
43
|
+
await super.onOpen(context);
|
|
44
|
+
scheduleTask(this._ctx, async () => {
|
|
45
|
+
try {
|
|
46
|
+
const result = await this.rpc.AdmissionDiscoveryService.getAdmissionCredential(this._request);
|
|
47
|
+
this._onResult.wake(result.admissionCredential);
|
|
48
|
+
} catch (err: any) {
|
|
49
|
+
context.close(err);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
override async onClose() {
|
|
55
|
+
await this._ctx.dispose();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override async onAbort(): Promise<void> {
|
|
59
|
+
await this._ctx.dispose();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class CredentialServerExtension extends RpcExtension<
|
|
64
|
+
{},
|
|
65
|
+
{ AdmissionDiscoveryService: AdmissionDiscoveryService }
|
|
66
|
+
> {
|
|
67
|
+
constructor(private readonly _space: Space) {
|
|
68
|
+
super({
|
|
69
|
+
exposed: {
|
|
70
|
+
AdmissionDiscoveryService: schema.getService('dxos.mesh.teleport.AdmissionDiscoveryService'),
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
protected override async getHandlers(): Promise<{ AdmissionDiscoveryService: AdmissionDiscoveryService }> {
|
|
76
|
+
return {
|
|
77
|
+
AdmissionDiscoveryService: {
|
|
78
|
+
getAdmissionCredential: async (
|
|
79
|
+
request: GetAdmissionCredentialRequest,
|
|
80
|
+
): Promise<GetAdmissionCredentialResponse> => {
|
|
81
|
+
const memberInfo = this._space.spaceState.members.get(request.memberKey);
|
|
82
|
+
if (!memberInfo?.credential) {
|
|
83
|
+
throw new ProtocolError('Space member not found.', request);
|
|
84
|
+
}
|
|
85
|
+
return { admissionCredential: memberInfo.credential };
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
package/src/space/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { synchronized, trackLeaks } from '@dxos/async';
|
|
5
|
+
import { synchronized, trackLeaks, Trigger } from '@dxos/async';
|
|
6
6
|
import { type DelegateInvitationCredential, type MemberInfo } from '@dxos/credentials';
|
|
7
7
|
import { failUndefined } from '@dxos/debug';
|
|
8
8
|
import { type FeedStore } from '@dxos/feed-store';
|
|
@@ -12,10 +12,12 @@ import { type SwarmNetworkManager } from '@dxos/network-manager';
|
|
|
12
12
|
import { trace } from '@dxos/protocols';
|
|
13
13
|
import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
14
14
|
import { type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
15
|
+
import type { Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
15
16
|
import { type Teleport } from '@dxos/teleport';
|
|
16
17
|
import { type BlobStore } from '@dxos/teleport-extension-object-sync';
|
|
17
18
|
import { ComplexMap } from '@dxos/util';
|
|
18
19
|
|
|
20
|
+
import { CredentialRetrieverExtension } from './admission-discovery-extension';
|
|
19
21
|
import { Space, createIdFromSpaceKey } from './space';
|
|
20
22
|
import { SpaceProtocol, type SwarmIdentity } from './space-protocol';
|
|
21
23
|
import { SnapshotManager, type SnapshotStore } from '../db-host';
|
|
@@ -47,6 +49,13 @@ export type ConstructSpaceParams = {
|
|
|
47
49
|
onMemberRolesChanged: (member: MemberInfo[]) => Promise<void>;
|
|
48
50
|
};
|
|
49
51
|
|
|
52
|
+
export type RequestSpaceAdmissionCredentialParams = {
|
|
53
|
+
spaceKey: PublicKey;
|
|
54
|
+
identityKey: PublicKey;
|
|
55
|
+
swarmIdentity: SwarmIdentity;
|
|
56
|
+
timeout: number;
|
|
57
|
+
};
|
|
58
|
+
|
|
50
59
|
/**
|
|
51
60
|
* Manages a collection of ECHO (Data) Spaces.
|
|
52
61
|
*/
|
|
@@ -126,4 +135,40 @@ export class SpaceManager {
|
|
|
126
135
|
log.trace('dxos.echo.space-manager.construct-space', trace.end({ id: this._instanceId }));
|
|
127
136
|
return space;
|
|
128
137
|
}
|
|
138
|
+
|
|
139
|
+
public async requestSpaceAdmissionCredential(params: RequestSpaceAdmissionCredentialParams): Promise<Credential> {
|
|
140
|
+
const traceKey = 'dxos.echo.space-manager.request-space-admission';
|
|
141
|
+
log.trace(traceKey, trace.begin({ id: this._instanceId }));
|
|
142
|
+
log('requesting space admission credential...', { spaceKey: params.spaceKey });
|
|
143
|
+
|
|
144
|
+
const onCredentialResolved = new Trigger<Credential>();
|
|
145
|
+
const protocol = new SpaceProtocol({
|
|
146
|
+
topic: params.spaceKey,
|
|
147
|
+
swarmIdentity: params.swarmIdentity,
|
|
148
|
+
networkManager: this._networkManager,
|
|
149
|
+
onSessionAuth: (session: Teleport) => {
|
|
150
|
+
session.addExtension(
|
|
151
|
+
'dxos.mesh.teleport.admission-discovery',
|
|
152
|
+
new CredentialRetrieverExtension(
|
|
153
|
+
{ spaceKey: params.spaceKey, memberKey: params.identityKey },
|
|
154
|
+
onCredentialResolved,
|
|
155
|
+
),
|
|
156
|
+
);
|
|
157
|
+
},
|
|
158
|
+
onAuthFailure: (session: Teleport) => session.close(),
|
|
159
|
+
blobStore: this._blobStore,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
await protocol.start();
|
|
164
|
+
const credential = await onCredentialResolved.wait({ timeout: params.timeout });
|
|
165
|
+
log.trace(traceKey, trace.end({ id: this._instanceId }));
|
|
166
|
+
return credential;
|
|
167
|
+
} catch (err: any) {
|
|
168
|
+
log.trace(traceKey, trace.error({ id: this._instanceId, error: err }));
|
|
169
|
+
throw err;
|
|
170
|
+
} finally {
|
|
171
|
+
await protocol.stop();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
129
174
|
}
|