@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.
Files changed (31) hide show
  1. package/dist/lib/browser/{chunk-I2J5TTHJ.mjs → chunk-HS77A4I4.mjs} +174 -23
  2. package/dist/lib/browser/{chunk-I2J5TTHJ.mjs.map → chunk-HS77A4I4.mjs.map} +4 -4
  3. package/dist/lib/browser/index.mjs +91 -42
  4. package/dist/lib/browser/index.mjs.map +3 -3
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +11 -7
  7. package/dist/lib/browser/testing/index.mjs.map +1 -1
  8. package/dist/lib/node/{chunk-QPCNQ4ZK.cjs → chunk-Y5U7UXEL.cjs} +185 -34
  9. package/dist/lib/node/{chunk-QPCNQ4ZK.cjs.map → chunk-Y5U7UXEL.cjs.map} +4 -4
  10. package/dist/lib/node/index.cjs +126 -77
  11. package/dist/lib/node/index.cjs.map +3 -3
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/testing/index.cjs +21 -17
  14. package/dist/lib/node/testing/index.cjs.map +1 -1
  15. package/dist/types/src/automerge/automerge-host.d.ts +40 -2
  16. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  17. package/dist/types/src/automerge/echo-network-adapter.d.ts +2 -0
  18. package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
  19. package/dist/types/src/space/admission-discovery-extension.d.ts +30 -0
  20. package/dist/types/src/space/admission-discovery-extension.d.ts.map +1 -0
  21. package/dist/types/src/space/index.d.ts +1 -0
  22. package/dist/types/src/space/index.d.ts.map +1 -1
  23. package/dist/types/src/space/space-manager.d.ts +8 -0
  24. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  25. package/package.json +33 -33
  26. package/src/automerge/automerge-host.ts +96 -18
  27. package/src/automerge/echo-network-adapter.ts +10 -4
  28. package/src/automerge/mesh-echo-replicator.ts +1 -1
  29. package/src/space/admission-discovery-extension.ts +90 -0
  30. package/src/space/index.ts +1 -0
  31. 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;AAExC,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;;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;CAoCxB"}
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.a2de4fa",
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.a2de4fa",
43
- "@dxos/automerge": "0.5.9-main.a2de4fa",
44
- "@dxos/context": "0.5.9-main.a2de4fa",
45
- "@dxos/codec-protobuf": "0.5.9-main.a2de4fa",
46
- "@dxos/credentials": "0.5.9-main.a2de4fa",
47
- "@dxos/crypto": "0.5.9-main.a2de4fa",
48
- "@dxos/debug": "0.5.9-main.a2de4fa",
49
- "@dxos/echo-protocol": "0.5.9-main.a2de4fa",
50
- "@dxos/echo-schema": "0.5.9-main.a2de4fa",
51
- "@dxos/hypercore": "0.5.9-main.a2de4fa",
52
- "@dxos/indexing": "0.5.9-main.a2de4fa",
53
- "@dxos/feed-store": "0.5.9-main.a2de4fa",
54
- "@dxos/invariant": "0.5.9-main.a2de4fa",
55
- "@dxos/keyring": "0.5.9-main.a2de4fa",
56
- "@dxos/keys": "0.5.9-main.a2de4fa",
57
- "@dxos/kv-store": "0.5.9-main.a2de4fa",
58
- "@dxos/log": "0.5.9-main.a2de4fa",
59
- "@dxos/messaging": "0.5.9-main.a2de4fa",
60
- "@dxos/network-manager": "0.5.9-main.a2de4fa",
61
- "@dxos/node-std": "0.5.9-main.a2de4fa",
62
- "@dxos/rpc": "0.5.9-main.a2de4fa",
63
- "@dxos/teleport": "0.5.9-main.a2de4fa",
64
- "@dxos/protocols": "0.5.9-main.a2de4fa",
65
- "@dxos/random-access-storage": "0.5.9-main.a2de4fa",
66
- "@dxos/teleport-extension-gossip": "0.5.9-main.a2de4fa",
67
- "@dxos/teleport-extension-automerge-replicator": "0.5.9-main.a2de4fa",
68
- "@dxos/teleport-extension-object-sync": "0.5.9-main.a2de4fa",
69
- "@dxos/timeframe": "0.5.9-main.a2de4fa",
70
- "@dxos/teleport-extension-replicator": "0.5.9-main.a2de4fa",
71
- "@dxos/tracing": "0.5.9-main.a2de4fa",
72
- "@dxos/typings": "0.5.9-main.a2de4fa",
73
- "@dxos/util": "0.5.9-main.a2de4fa"
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 { next as automerge, getBackend, getHeads, type Doc, type Heads } from '@dxos/automerge/automerge';
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 as any)?.dxos_peerSource === 'EchoNetworkAdapter') {
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
- // Methods for client-services.
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
- await Promise.all(
222
- states?.map(async ({ heads, documentId }) => {
223
- invariant(heads, 'heads are required for flush');
224
- const handle = this.repo.handles[documentId as DocumentId] ?? this._repo.find(documentId as DocumentId);
225
- await waitForHeads(handle, heads);
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: any): string | null => {
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
+ }
@@ -6,3 +6,4 @@ export * from './auth';
6
6
  export * from './space';
7
7
  export * from './space-manager';
8
8
  export * from './space-protocol';
9
+ export * from './admission-discovery-extension';
@@ -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
  }