@dxos/echo-pipeline 0.5.9-next.a50ff17 → 0.6.0

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 (36) 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 +182 -126
  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 +230 -174
  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-doc-loader.d.ts +2 -1
  16. package/dist/types/src/automerge/automerge-doc-loader.d.ts.map +1 -1
  17. package/dist/types/src/automerge/automerge-host.d.ts +40 -3
  18. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  19. package/dist/types/src/automerge/echo-network-adapter.d.ts +2 -0
  20. package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
  21. package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
  22. package/dist/types/src/space/admission-discovery-extension.d.ts +30 -0
  23. package/dist/types/src/space/admission-discovery-extension.d.ts.map +1 -0
  24. package/dist/types/src/space/index.d.ts +1 -0
  25. package/dist/types/src/space/index.d.ts.map +1 -1
  26. package/dist/types/src/space/space-manager.d.ts +8 -0
  27. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  28. package/package.json +33 -33
  29. package/src/automerge/automerge-doc-loader.test.ts +8 -5
  30. package/src/automerge/automerge-doc-loader.ts +29 -20
  31. package/src/automerge/automerge-host.ts +96 -32
  32. package/src/automerge/echo-network-adapter.ts +10 -4
  33. package/src/automerge/mesh-echo-replicator.ts +7 -2
  34. package/src/space/admission-discovery-extension.ts +90 -0
  35. package/src/space/index.ts +1 -0
  36. package/src/space/space-manager.ts +46 -1
@@ -0,0 +1,30 @@
1
+ import { type Trigger } from '@dxos/async';
2
+ import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
3
+ import { type AdmissionDiscoveryService, type GetAdmissionCredentialRequest } from '@dxos/protocols/proto/dxos/mesh/teleport';
4
+ import { type ExtensionContext, RpcExtension } from '@dxos/teleport';
5
+ import { type Space } from './space';
6
+ /**
7
+ * Guest's side for a connection to a concrete peer in p2p network during invitation.
8
+ */
9
+ export declare class CredentialRetrieverExtension extends RpcExtension<{
10
+ AdmissionDiscoveryService: AdmissionDiscoveryService;
11
+ }, {}> {
12
+ private readonly _request;
13
+ private readonly _onResult;
14
+ private _ctx;
15
+ constructor(_request: GetAdmissionCredentialRequest, _onResult: Trigger<Credential>);
16
+ protected getHandlers(): Promise<{}>;
17
+ onOpen(context: ExtensionContext): Promise<void>;
18
+ onClose(): Promise<void>;
19
+ onAbort(): Promise<void>;
20
+ }
21
+ export declare class CredentialServerExtension extends RpcExtension<{}, {
22
+ AdmissionDiscoveryService: AdmissionDiscoveryService;
23
+ }> {
24
+ private readonly _space;
25
+ constructor(_space: Space);
26
+ protected getHandlers(): Promise<{
27
+ AdmissionDiscoveryService: AdmissionDiscoveryService;
28
+ }>;
29
+ }
30
+ //# sourceMappingURL=admission-discovery-extension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admission-discovery-extension.d.ts","sourceRoot":"","sources":["../../../../src/space/admission-discovery-extension.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EACL,KAAK,yBAAyB,EAE9B,KAAK,6BAA6B,EACnC,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,KAAK,gBAAgB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC;;GAEG;AACH,qBAAa,4BAA6B,SAAQ,YAAY,CAC5D;IAAE,yBAAyB,EAAE,yBAAyB,CAAA;CAAE,EACxD,EAAE,CACH;IAIG,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJ5B,OAAO,CAAC,IAAI,CAAiB;gBAGV,QAAQ,EAAE,6BAA6B,EACvC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC;cASxB,WAAW,IAAI,OAAO,CAAC,EAAE,CAAC;IAIpC,MAAM,CAAC,OAAO,EAAE,gBAAgB;IAYhC,OAAO;IAIP,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAGxC;AAED,qBAAa,yBAA0B,SAAQ,YAAY,CACzD,EAAE,EACF;IAAE,yBAAyB,EAAE,yBAAyB,CAAA;CAAE,CACzD;IACa,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,KAAK;cAQjB,WAAW,IAAI,OAAO,CAAC;QAAE,yBAAyB,EAAE,yBAAyB,CAAA;KAAE,CAAC;CAe1G"}
@@ -2,4 +2,5 @@ export * from './auth';
2
2
  export * from './space';
3
3
  export * from './space-manager';
4
4
  export * from './space-protocol';
5
+ export * from './admission-discovery-extension';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/space/index.ts"],"names":[],"mappings":"AAIA,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/space/index.ts"],"names":[],"mappings":"AAIA,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iCAAiC,CAAC"}
@@ -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-next.a50ff17",
3
+ "version": "0.6.0",
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-next.a50ff17",
43
- "@dxos/codec-protobuf": "0.5.9-next.a50ff17",
44
- "@dxos/context": "0.5.9-next.a50ff17",
45
- "@dxos/automerge": "0.5.9-next.a50ff17",
46
- "@dxos/credentials": "0.5.9-next.a50ff17",
47
- "@dxos/crypto": "0.5.9-next.a50ff17",
48
- "@dxos/debug": "0.5.9-next.a50ff17",
49
- "@dxos/echo-protocol": "0.5.9-next.a50ff17",
50
- "@dxos/feed-store": "0.5.9-next.a50ff17",
51
- "@dxos/echo-schema": "0.5.9-next.a50ff17",
52
- "@dxos/hypercore": "0.5.9-next.a50ff17",
53
- "@dxos/indexing": "0.5.9-next.a50ff17",
54
- "@dxos/invariant": "0.5.9-next.a50ff17",
55
- "@dxos/keyring": "0.5.9-next.a50ff17",
56
- "@dxos/kv-store": "0.5.9-next.a50ff17",
57
- "@dxos/log": "0.5.9-next.a50ff17",
58
- "@dxos/messaging": "0.5.9-next.a50ff17",
59
- "@dxos/network-manager": "0.5.9-next.a50ff17",
60
- "@dxos/keys": "0.5.9-next.a50ff17",
61
- "@dxos/protocols": "0.5.9-next.a50ff17",
62
- "@dxos/random-access-storage": "0.5.9-next.a50ff17",
63
- "@dxos/node-std": "0.5.9-next.a50ff17",
64
- "@dxos/rpc": "0.5.9-next.a50ff17",
65
- "@dxos/teleport-extension-automerge-replicator": "0.5.9-next.a50ff17",
66
- "@dxos/teleport-extension-gossip": "0.5.9-next.a50ff17",
67
- "@dxos/teleport-extension-object-sync": "0.5.9-next.a50ff17",
68
- "@dxos/teleport": "0.5.9-next.a50ff17",
69
- "@dxos/teleport-extension-replicator": "0.5.9-next.a50ff17",
70
- "@dxos/typings": "0.5.9-next.a50ff17",
71
- "@dxos/timeframe": "0.5.9-next.a50ff17",
72
- "@dxos/util": "0.5.9-next.a50ff17",
73
- "@dxos/tracing": "0.5.9-next.a50ff17"
42
+ "@dxos/async": "0.6.0",
43
+ "@dxos/automerge": "0.6.0",
44
+ "@dxos/context": "0.6.0",
45
+ "@dxos/codec-protobuf": "0.6.0",
46
+ "@dxos/credentials": "0.6.0",
47
+ "@dxos/crypto": "0.6.0",
48
+ "@dxos/debug": "0.6.0",
49
+ "@dxos/echo-schema": "0.6.0",
50
+ "@dxos/echo-protocol": "0.6.0",
51
+ "@dxos/indexing": "0.6.0",
52
+ "@dxos/feed-store": "0.6.0",
53
+ "@dxos/hypercore": "0.6.0",
54
+ "@dxos/invariant": "0.6.0",
55
+ "@dxos/keyring": "0.6.0",
56
+ "@dxos/keys": "0.6.0",
57
+ "@dxos/kv-store": "0.6.0",
58
+ "@dxos/log": "0.6.0",
59
+ "@dxos/node-std": "0.6.0",
60
+ "@dxos/network-manager": "0.6.0",
61
+ "@dxos/messaging": "0.6.0",
62
+ "@dxos/protocols": "0.6.0",
63
+ "@dxos/random-access-storage": "0.6.0",
64
+ "@dxos/teleport": "0.6.0",
65
+ "@dxos/teleport-extension-automerge-replicator": "0.6.0",
66
+ "@dxos/rpc": "0.6.0",
67
+ "@dxos/teleport-extension-object-sync": "0.6.0",
68
+ "@dxos/teleport-extension-gossip": "0.6.0",
69
+ "@dxos/teleport-extension-replicator": "0.6.0",
70
+ "@dxos/timeframe": "0.6.0",
71
+ "@dxos/tracing": "0.6.0",
72
+ "@dxos/util": "0.6.0",
73
+ "@dxos/typings": "0.6.0"
74
74
  },
75
75
  "devDependencies": {
76
76
  "fast-check": "^3.19.0",
@@ -7,7 +7,7 @@ import { expect } from 'chai';
7
7
  import { sleep } from '@dxos/async';
8
8
  import { Repo } from '@dxos/automerge/automerge-repo';
9
9
  import { Context } from '@dxos/context';
10
- import { type SpaceDoc } from '@dxos/echo-protocol';
10
+ import { type SpaceDoc, SpaceDocVersion } from '@dxos/echo-protocol';
11
11
  import { generateEchoId } from '@dxos/echo-schema';
12
12
  import { PublicKey } from '@dxos/keys';
13
13
  import { describe, test } from '@dxos/test';
@@ -22,6 +22,7 @@ import { createIdFromSpaceKey } from '../space';
22
22
  const ctx = new Context();
23
23
  const SPACE_KEY = PublicKey.random();
24
24
  const randomId = () => generateEchoId();
25
+
25
26
  describe('AutomergeDocumentLoader', () => {
26
27
  test('space access is set on root doc handle and it is accessible', async () => {
27
28
  const { loader, spaceRootDocHandle } = await setupTest();
@@ -76,13 +77,15 @@ describe('AutomergeDocumentLoader', () => {
76
77
  const spaceId = await createIdFromSpaceKey(SPACE_KEY);
77
78
  const repo = new Repo({ network: [] });
78
79
  const loader = new AutomergeDocumentLoaderImpl(spaceId, repo, SPACE_KEY);
79
- const spaceRootDocHandle = repo.create<SpaceDoc>();
80
- await loader.loadSpaceRootDocHandle(ctx, {
81
- rootUrl: spaceRootDocHandle.url,
82
- });
80
+ const spaceRootDocHandle = createRootDoc(repo);
81
+ await loader.loadSpaceRootDocHandle(ctx, { rootUrl: spaceRootDocHandle.url });
83
82
  return { loader, spaceRootDocHandle, repo };
84
83
  };
85
84
 
85
+ const createRootDoc = (repo: Repo) => {
86
+ return repo.create<SpaceDoc>({ version: SpaceDocVersion.CURRENT });
87
+ };
88
+
86
89
  const loadLinkedObjects = (loader: AutomergeDocumentLoader, links: SpaceDoc['links']) => {
87
90
  Object.keys(links ?? {}).forEach((objectId) => loader.loadObjectDocument(objectId));
88
91
  loader.onObjectLinksUpdated(links);
@@ -3,7 +3,13 @@
3
3
  //
4
4
 
5
5
  import { Event } from '@dxos/async';
6
- import { type DocHandle, type AutomergeUrl, type DocumentId, type Repo } from '@dxos/automerge/automerge-repo';
6
+ import {
7
+ type DocHandle,
8
+ type AutomergeUrl,
9
+ type DocumentId,
10
+ type Repo,
11
+ interpretAsDocumentId,
12
+ } from '@dxos/automerge/automerge-repo';
7
13
  import { cancelWithContext, type Context } from '@dxos/context';
8
14
  import { warnAfterTimeout } from '@dxos/debug';
9
15
  import { type SpaceState, type SpaceDoc, SpaceDocVersion } from '@dxos/echo-protocol';
@@ -21,6 +27,7 @@ export interface AutomergeDocumentLoader {
21
27
 
22
28
  loadSpaceRootDocHandle(ctx: Context, spaceState: SpaceState): Promise<void>;
23
29
  loadObjectDocument(objectId: string | string[]): void;
30
+ getObjectDocumentId(objectId: string): string | undefined;
24
31
  getSpaceRootDocHandle(): DocHandle<SpaceDoc>;
25
32
  createDocumentForObject(objectId: string): DocHandle<SpaceDoc>;
26
33
  onObjectLinksUpdated(links: SpaceDocumentLinks): void;
@@ -69,17 +76,17 @@ export class AutomergeDocumentLoaderImpl implements AutomergeDocumentLoader {
69
76
  return;
70
77
  }
71
78
  if (!spaceState.rootUrl) {
72
- log.error('Database opened with no rootUrl', { spaceId: this._spaceId });
73
- this._createContextBoundSpaceRootDocument(ctx);
74
- } else {
75
- const existingDocHandle = await this._initDocHandle(ctx, spaceState.rootUrl);
76
- const doc = existingDocHandle.docSync();
77
- invariant(doc);
78
- if (doc.access == null) {
79
- this._initDocAccess(existingDocHandle);
80
- }
81
- this._spaceRootDocHandle = existingDocHandle;
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);
82
88
  }
89
+ this._spaceRootDocHandle = existingDocHandle;
83
90
  }
84
91
 
85
92
  public loadObjectDocument(objectIdOrMany: string | string[]) {
@@ -107,6 +114,17 @@ export class AutomergeDocumentLoaderImpl implements AutomergeDocumentLoader {
107
114
  }
108
115
  }
109
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
+
110
128
  public onObjectLinksUpdated(links: SpaceDocumentLinks) {
111
129
  if (!links) {
112
130
  return;
@@ -198,15 +216,6 @@ export class AutomergeDocumentLoaderImpl implements AutomergeDocumentLoader {
198
216
  return docHandle;
199
217
  }
200
218
 
201
- private _createContextBoundSpaceRootDocument(ctx: Context) {
202
- const docHandle = this._repo.create<SpaceDoc>();
203
- this._spaceRootDocHandle = docHandle;
204
- ctx.onDispose(() => {
205
- docHandle.delete();
206
- this._spaceRootDocHandle = null;
207
- });
208
- }
209
-
210
219
  private _initDocAccess(handle: DocHandle<SpaceDoc>) {
211
220
  handle.change((newDoc: SpaceDoc) => {
212
221
  newDoc.access ??= { spaceKey: this._spaceKey.toHex() };
@@ -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,13 +22,11 @@ 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
- import { log } from '@dxos/log';
23
30
  import { objectPointerCodec } from '@dxos/protocols';
24
31
  import {
25
32
  type FlushRequest,
@@ -30,7 +37,7 @@ import {
30
37
  import { trace } from '@dxos/tracing';
31
38
  import { mapValues } from '@dxos/util';
32
39
 
33
- import { EchoNetworkAdapter } from './echo-network-adapter';
40
+ import { EchoNetworkAdapter, isEchoPeerMetadata } from './echo-network-adapter';
34
41
  import { type EchoReplicator } from './echo-replicator';
35
42
  import { LevelDBStorageAdapter, type BeforeSaveParams } from './leveldb-storage-adapter';
36
43
  import { LocalHostNetworkAdapter } from './local-host-network-adapter';
@@ -44,6 +51,20 @@ export type AutomergeHostParams = {
44
51
  indexMetadataStore: IndexMetadataStore;
45
52
  };
46
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
+ */
47
68
  @trace.resource()
48
69
  export class AutomergeHost {
49
70
  private readonly _indexMetadataStore: IndexMetadataStore;
@@ -59,8 +80,6 @@ export class AutomergeHost {
59
80
  @trace.info()
60
81
  private _peerId!: string;
61
82
 
62
- public _requestedDocs = new Set<string>();
63
-
64
83
  constructor({ db, indexMetadataStore }: AutomergeHostParams) {
65
84
  this._storage = new LevelDBStorageAdapter({
66
85
  db,
@@ -105,6 +124,9 @@ export class AutomergeHost {
105
124
  await this._ctx.dispose();
106
125
  }
107
126
 
127
+ /**
128
+ * @deprecated To be abstracted away.
129
+ */
108
130
  get repo(): Repo {
109
131
  return this._repo;
110
132
  }
@@ -117,6 +139,46 @@ export class AutomergeHost {
117
139
  await this._echoNetworkAdapter.removeReplicator(replicator);
118
140
  }
119
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
+
120
182
  // TODO(dmaretskyi): Share based on HALO permissions and space affinity.
121
183
  // Hosts, running in the worker, don't share documents unless requested by other peers.
122
184
  // NOTE: If both peers return sharePolicy=false the replication will not happen
@@ -133,19 +195,8 @@ export class AutomergeHost {
133
195
  return false;
134
196
  }
135
197
 
136
- // Workaround for https://github.com/automerge/automerge-repo/pull/292
137
- // NOTE: This must override the per-connection policy.
138
- const doc = this._repo.handles[documentId]?.docSync();
139
- if (!doc) {
140
- // TODO(dmaretskyi): Verify that this works as intended.
141
- // TODO(dmaretskyi): Move to MESH replicator?
142
- const isRequested = this._requestedDocs.has(`automerge:${documentId}`);
143
- log('doc share policy check', { peerId, documentId, isRequested });
144
- return isRequested;
145
- }
146
-
147
198
  const peerMetadata = this.repo.peerMetadataByPeerId[peerId];
148
- if ((peerMetadata as any)?.dxos_peerSource === 'EchoNetworkAdapter') {
199
+ if (isEchoPeerMetadata(peerMetadata)) {
149
200
  return this._echoNetworkAdapter.shouldAdvertize(peerId, { documentId });
150
201
  }
151
202
 
@@ -226,39 +277,52 @@ export class AutomergeHost {
226
277
  return PublicKey.from(spaceKeyHex);
227
278
  }
228
279
 
229
- //
230
- // Methods for client-services.
231
- //
280
+ /**
281
+ * Flush documents to disk.
282
+ */
232
283
  @trace.span({ showInBrowserTimeline: true })
233
284
  async flush({ states }: FlushRequest): Promise<void> {
234
285
  // Note: Wait for all requested documents to be loaded/synced from thin-client.
235
- await Promise.all(
236
- states?.map(async ({ heads, documentId }) => {
237
- invariant(heads, 'heads are required for flush');
238
- const handle = this.repo.handles[documentId as DocumentId] ?? this._repo.find(documentId as DocumentId);
239
- await waitForHeads(handle, heads);
240
- }) ?? [],
241
- );
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
+ }
242
297
 
243
298
  await this._repo.flush(states?.map(({ documentId }) => documentId as DocumentId));
244
299
  }
245
300
 
301
+ /**
302
+ * Host <-> Client sync.
303
+ */
246
304
  syncRepo(request: SyncRepoRequest): Stream<SyncRepoResponse> {
247
305
  return this._clientNetwork.syncRepo(request);
248
306
  }
249
307
 
308
+ /**
309
+ * Host <-> Client sync.
310
+ */
250
311
  sendSyncMessage(request: SyncRepoRequest): Promise<void> {
251
312
  return this._clientNetwork.sendSyncMessage(request);
252
313
  }
253
314
 
315
+ /**
316
+ * Host <-> Client sync.
317
+ */
254
318
  async getHostInfo(): Promise<HostInfo> {
255
319
  return this._clientNetwork.getHostInfo();
256
320
  }
257
321
  }
258
322
 
259
- export const getSpaceKeyFromDoc = (doc: any): string | null => {
323
+ export const getSpaceKeyFromDoc = (doc: Doc<SpaceDoc>): string | null => {
260
324
  // experimental_spaceKey is set on old documents, new ones are created with doc.access.spaceKey
261
- const rawSpaceKey = doc.access?.spaceKey ?? doc.experimental_spaceKey;
325
+ const rawSpaceKey = doc.access?.spaceKey ?? (doc as any).experimental_spaceKey;
262
326
  if (rawSpaceKey == null) {
263
327
  return null;
264
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';
@@ -63,15 +63,15 @@ export class MeshEchoReplicator implements EchoReplicator {
63
63
  this._context.onConnectionAuthScopeChanged(connection);
64
64
  } else {
65
65
  this._connectionsPerPeer.set(connection.peerId, connection);
66
- await connection.enable();
67
66
  this._context.onConnectionOpen(connection);
67
+ await connection.enable();
68
68
  }
69
69
  },
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) => {
@@ -121,6 +121,11 @@ export class MeshEchoReplicator implements EchoReplicator {
121
121
  authorizeDevice(spaceKey: PublicKey, deviceKey: PublicKey) {
122
122
  log('authorizeDevice', { spaceKey, deviceKey });
123
123
  defaultMap(this._authorizedDevices, spaceKey, () => new ComplexSet(PublicKey.hash)).add(deviceKey);
124
+ for (const connection of this._connections) {
125
+ if (connection.remoteDeviceKey && connection.remoteDeviceKey.equals(deviceKey)) {
126
+ this._context?.onConnectionAuthScopeChanged(connection);
127
+ }
128
+ }
124
129
  }
125
130
  }
126
131
 
@@ -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';