@dxos/client-services 0.4.10-main.c75170d → 0.4.10-main.c8e5c39

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 (69) hide show
  1. package/dist/lib/browser/{chunk-JP7F2IH3.mjs → chunk-7OKNHCYB.mjs} +425 -410
  2. package/dist/lib/browser/chunk-7OKNHCYB.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +6 -4
  4. package/dist/lib/browser/index.mjs.map +3 -3
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/packlets/testing/index.mjs +131 -116
  7. package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/{chunk-34EZSH65.cjs → chunk-5JA576YH.cjs} +527 -508
  9. package/dist/lib/node/chunk-5JA576YH.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +48 -46
  11. package/dist/lib/node/index.cjs.map +3 -3
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/packlets/testing/index.cjs +130 -118
  14. package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
  15. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  16. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +3 -1
  17. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
  18. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +6 -1
  19. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
  20. package/dist/types/src/packlets/invitations/invitations-handler.d.ts +4 -2
  21. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  22. package/dist/types/src/packlets/invitations/invitations-manager.d.ts +9 -7
  23. package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
  24. package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
  25. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +2 -1
  26. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  27. package/dist/types/src/packlets/services/service-context.d.ts +4 -6
  28. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  29. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  30. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +8 -3
  31. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  32. package/dist/types/src/packlets/spaces/data-space.d.ts +4 -3
  33. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  34. package/dist/types/src/packlets/storage/level.d.ts.map +1 -1
  35. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  36. package/dist/types/src/packlets/testing/test-builder.d.ts +7 -3
  37. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  38. package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
  39. package/dist/types/src/version.d.ts +1 -1
  40. package/package.json +34 -34
  41. package/src/packlets/identity/identity-manager.ts +1 -0
  42. package/src/packlets/identity/identity.test.ts +3 -0
  43. package/src/packlets/invitations/device-invitation-protocol.ts +6 -1
  44. package/src/packlets/invitations/invitation-protocol.ts +7 -1
  45. package/src/packlets/invitations/invitations-handler.ts +10 -71
  46. package/src/packlets/invitations/invitations-manager.ts +114 -40
  47. package/src/packlets/invitations/invitations-service.ts +4 -2
  48. package/src/packlets/invitations/space-invitation-protocol.ts +45 -3
  49. package/src/packlets/services/automerge-host.test.ts +4 -4
  50. package/src/packlets/services/service-context.test.ts +3 -3
  51. package/src/packlets/services/service-context.ts +12 -25
  52. package/src/packlets/services/service-host.test.ts +6 -0
  53. package/src/packlets/services/service-host.ts +5 -16
  54. package/src/packlets/spaces/data-space-manager.test.ts +4 -4
  55. package/src/packlets/spaces/data-space-manager.ts +56 -13
  56. package/src/packlets/spaces/data-space.ts +14 -19
  57. package/src/packlets/storage/level.ts +1 -0
  58. package/src/packlets/testing/invitation-utils.ts +100 -97
  59. package/src/packlets/testing/test-builder.ts +27 -14
  60. package/src/packlets/vault/worker-runtime.ts +3 -1
  61. package/src/version.ts +1 -1
  62. package/dist/lib/browser/chunk-JP7F2IH3.mjs.map +0 -7
  63. package/dist/lib/node/chunk-34EZSH65.cjs.map +0 -7
  64. package/dist/types/src/packlets/indexing/index.d.ts +0 -2
  65. package/dist/types/src/packlets/indexing/index.d.ts.map +0 -1
  66. package/dist/types/src/packlets/indexing/util.d.ts +0 -16
  67. package/dist/types/src/packlets/indexing/util.d.ts.map +0 -1
  68. package/src/packlets/indexing/index.ts +0 -5
  69. package/src/packlets/indexing/util.ts +0 -89
@@ -4,18 +4,20 @@
4
4
 
5
5
  import { Event, synchronized, trackLeaks } from '@dxos/async';
6
6
  import { Context, cancelWithContext } from '@dxos/context';
7
- import { getCredentialAssertion, type CredentialSigner } from '@dxos/credentials';
8
- import { type AutomergeHost, type MetadataStore, type Space, type SpaceManager } from '@dxos/echo-pipeline';
7
+ import { getCredentialAssertion, type CredentialSigner, type DelegateInvitationCredential } from '@dxos/credentials';
8
+ import { type EchoHost } from '@dxos/echo-db';
9
+ import { type MetadataStore, type Space, type SpaceManager } from '@dxos/echo-pipeline';
9
10
  import { type FeedStore } from '@dxos/feed-store';
10
11
  import { invariant } from '@dxos/invariant';
11
12
  import { type Keyring } from '@dxos/keyring';
12
13
  import { PublicKey } from '@dxos/keys';
13
14
  import { log } from '@dxos/log';
14
15
  import { trace } from '@dxos/protocols';
15
- import { SpaceState } from '@dxos/protocols/proto/dxos/client/services';
16
+ import { Invitation, SpaceState } from '@dxos/protocols/proto/dxos/client/services';
16
17
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
17
18
  import { type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
18
19
  import { type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
20
+ import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
19
21
  import { Gossip, Presence } from '@dxos/teleport-extension-gossip';
20
22
  import { type Timeframe } from '@dxos/timeframe';
21
23
  import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
@@ -23,6 +25,7 @@ import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
23
25
  import { DataSpace } from './data-space';
24
26
  import { spaceGenesis } from './genesis';
25
27
  import { createAuthProvider } from '../identity';
28
+ import { type InvitationsManager } from '../invitations';
26
29
 
27
30
  const PRESENCE_ANNOUNCE_INTERVAL = 10_000;
28
31
  const PRESENCE_OFFLINE_TIMEOUT = 20_000;
@@ -77,7 +80,8 @@ export class DataSpaceManager {
77
80
  private readonly _keyring: Keyring,
78
81
  private readonly _signingContext: SigningContext,
79
82
  private readonly _feedStore: FeedStore<FeedMessage>,
80
- private readonly _automergeHost: AutomergeHost,
83
+ private readonly _echoHost: EchoHost,
84
+ private readonly _invitationsManager: InvitationsManager,
81
85
  params?: DataSpaceManagerRuntimeParams,
82
86
  ) {
83
87
  const {
@@ -149,14 +153,10 @@ export class DataSpaceManager {
149
153
 
150
154
  log('creating space...', { spaceKey });
151
155
 
152
- const automergeRoot = this._automergeHost.repo.create();
153
- automergeRoot.change((doc: any) => {
154
- doc.access = { spaceKey: spaceKey.toHex() };
155
- });
156
-
156
+ const automergeRootUrl = await this._echoHost.createSpaceRoot(spaceKey);
157
157
  const space = await this._constructSpace(metadata);
158
158
 
159
- const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner, automergeRoot.url);
159
+ const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner, automergeRootUrl);
160
160
  await this._metadataStore.addSpace(metadata);
161
161
 
162
162
  const memberCredential = credentials[1];
@@ -240,13 +240,16 @@ export class DataSpaceManager {
240
240
  gossip.createExtension({ remotePeerId: session.remotePeerId }),
241
241
  );
242
242
  session.addExtension('dxos.mesh.teleport.notarization', dataSpace.notarizationPlugin.createExtension());
243
- this._automergeHost.authorizeDevice(space.key, session.remotePeerId);
244
- session.addExtension('dxos.mesh.teleport.automerge', this._automergeHost.createExtension());
243
+ this._echoHost.authorizeDevice(space.key, session.remotePeerId);
244
+ session.addExtension('dxos.mesh.teleport.automerge', this._echoHost.createReplicationExtension());
245
245
  },
246
246
  onAuthFailure: () => {
247
247
  log.warn('auth failure');
248
248
  },
249
249
  memberKey: this._signingContext.identityKey,
250
+ onDelegatedInvitationStatusChange: (invitation, isActive) => {
251
+ return this._handleInvitationStatusChange(dataSpace, invitation, isActive);
252
+ },
250
253
  });
251
254
  controlFeed && (await space.setControlFeed(controlFeed));
252
255
  dataFeed && (await space.setDataFeed(dataFeed));
@@ -259,6 +262,7 @@ export class DataSpaceManager {
259
262
  presence,
260
263
  keyring: this._keyring,
261
264
  feedStore: this._feedStore,
265
+ echoHost: this._echoHost,
262
266
  signingContext: this._signingContext,
263
267
  callbacks: {
264
268
  beforeReady: async () => {
@@ -267,6 +271,7 @@ export class DataSpaceManager {
267
271
  afterReady: async () => {
268
272
  log('after space ready', { space: space.key, open: this._isOpen });
269
273
  if (this._isOpen) {
274
+ await this._createDelegatedInvitations(dataSpace, [...space.spaceState.invitations.entries()]);
270
275
  this.updated.emit();
271
276
  }
272
277
  },
@@ -275,7 +280,6 @@ export class DataSpaceManager {
275
280
  },
276
281
  },
277
282
  cache: metadata.cache,
278
- automergeHost: this._automergeHost,
279
283
  });
280
284
 
281
285
  if (metadata.state !== SpaceState.INACTIVE) {
@@ -289,4 +293,43 @@ export class DataSpaceManager {
289
293
  this._spaces.set(metadata.key, dataSpace);
290
294
  return dataSpace;
291
295
  }
296
+
297
+ private async _handleInvitationStatusChange(
298
+ dataSpace: DataSpace | undefined,
299
+ delegatedInvitation: DelegateInvitationCredential,
300
+ isActive: boolean,
301
+ ): Promise<void> {
302
+ if (dataSpace?.state !== SpaceState.READY) {
303
+ return;
304
+ }
305
+ if (isActive) {
306
+ await this._createDelegatedInvitations(dataSpace, [
307
+ [delegatedInvitation.credentialId, delegatedInvitation.invitation],
308
+ ]);
309
+ } else {
310
+ await this._invitationsManager.cancelInvitation(delegatedInvitation.invitation);
311
+ }
312
+ }
313
+
314
+ private async _createDelegatedInvitations(
315
+ space: DataSpace,
316
+ invitations: Array<[PublicKey, DelegateSpaceInvitation]>,
317
+ ): Promise<void> {
318
+ const tasks = invitations.map(([credentialId, invitation]) => {
319
+ return this._invitationsManager.createInvitation({
320
+ type: Invitation.Type.DELEGATED,
321
+ kind: Invitation.Kind.SPACE,
322
+ spaceKey: space.key,
323
+ authMethod: invitation.authMethod,
324
+ invitationId: invitation.invitationId,
325
+ swarmKey: invitation.swarmKey,
326
+ guestKeypair: invitation.guestKey ? { publicKey: invitation.guestKey } : undefined,
327
+ lifetime: invitation.expiresOn ? invitation.expiresOn.getTime() - Date.now() : undefined,
328
+ multiUse: invitation.multiUse,
329
+ delegationCredentialId: credentialId,
330
+ persistent: false,
331
+ });
332
+ });
333
+ await Promise.all(tasks);
334
+ }
292
335
  }
@@ -6,15 +6,10 @@ import { Event, asyncTimeout, scheduleTask, sleep, synchronized, trackLeaks } fr
6
6
  import { AUTH_TIMEOUT } from '@dxos/client-protocol';
7
7
  import { cancelWithContext, Context, ContextDisposedError } from '@dxos/context';
8
8
  import { timed, warnAfterTimeout } from '@dxos/debug';
9
- import { TYPE_PROPERTIES } from '@dxos/echo-db';
10
- import {
11
- type MetadataStore,
12
- type Space,
13
- createMappedFeedWriter,
14
- type AutomergeHost,
15
- type SpaceDoc,
16
- } from '@dxos/echo-pipeline';
9
+ import { type EchoHost } from '@dxos/echo-db';
10
+ import { type MetadataStore, type Space, createMappedFeedWriter, type SpaceDoc } from '@dxos/echo-pipeline';
17
11
  import { AutomergeDocumentLoaderImpl } from '@dxos/echo-pipeline';
12
+ import { TYPE_PROPERTIES } from '@dxos/echo-schema';
18
13
  import { type FeedStore } from '@dxos/feed-store';
19
14
  import { failedInvariant, invariant } from '@dxos/invariant';
20
15
  import { type Keyring } from '@dxos/keyring';
@@ -66,10 +61,10 @@ export type DataSpaceParams = {
66
61
  presence: Presence;
67
62
  keyring: Keyring;
68
63
  feedStore: FeedStore<FeedMessage>;
64
+ echoHost: EchoHost;
69
65
  signingContext: SigningContext;
70
66
  callbacks?: DataSpaceCallbacks;
71
67
  cache?: SpaceCache;
72
- automergeHost: AutomergeHost;
73
68
  };
74
69
 
75
70
  export type CreateEpochOptions = {
@@ -92,7 +87,7 @@ export class DataSpace {
92
87
  private readonly _notarizationPlugin = new NotarizationPlugin();
93
88
  private readonly _callbacks: DataSpaceCallbacks;
94
89
  private readonly _cache?: SpaceCache = undefined;
95
- private readonly _automergeHost: AutomergeHost;
90
+ private readonly _echoHost: EchoHost;
96
91
 
97
92
  // TODO(dmaretskyi): Move into Space?
98
93
  private readonly _automergeSpaceState = new AutomergeSpaceState((rootUrl) => this._onNewAutomergeRoot(rootUrl));
@@ -120,7 +115,7 @@ export class DataSpace {
120
115
  this._metadataStore = params.metadataStore;
121
116
  this._signingContext = params.signingContext;
122
117
  this._callbacks = params.callbacks ?? {};
123
- this._automergeHost = params.automergeHost;
118
+ this._echoHost = params.echoHost;
124
119
 
125
120
  this.authVerifier = new TrustedKeySetAuthVerifier({
126
121
  trustedKeysProvider: () =>
@@ -363,8 +358,8 @@ export class DataSpace {
363
358
 
364
359
  private _onNewAutomergeRoot(rootUrl: string) {
365
360
  log('loading automerge root doc for space', { space: this.key, rootUrl });
366
- this._automergeHost._requestedDocs.add(rootUrl as any);
367
- const handle = this._automergeHost.repo.find(rootUrl as any);
361
+ this._echoHost.replicateDocument(rootUrl);
362
+ const handle = this._echoHost.automergeRepo.find(rootUrl as any);
368
363
 
369
364
  queueMicrotask(async () => {
370
365
  try {
@@ -419,7 +414,7 @@ export class DataSpace {
419
414
  break;
420
415
  case CreateEpochRequest.Migration.INIT_AUTOMERGE:
421
416
  {
422
- const document = this._automergeHost.repo.create();
417
+ const document = this._echoHost.automergeRepo.create();
423
418
  // TODO(dmaretskyi): Unify epoch construction.
424
419
  epoch = {
425
420
  previousId: this._automergeSpaceState.lastEpoch?.id,
@@ -432,9 +427,9 @@ export class DataSpace {
432
427
  case CreateEpochRequest.Migration.PRUNE_AUTOMERGE_ROOT_HISTORY:
433
428
  {
434
429
  const currentRootUrl = this._automergeSpaceState.rootUrl;
435
- const rootHandle = this._automergeHost.repo.find(currentRootUrl as any);
430
+ const rootHandle = this._echoHost.automergeRepo.find(currentRootUrl as any);
436
431
  await cancelWithContext(this._ctx, asyncTimeout(rootHandle.whenReady(), 10_000));
437
- const newRoot = this._automergeHost.repo.create(rootHandle.docSync());
432
+ const newRoot = this._echoHost.automergeRepo.create(rootHandle.docSync());
438
433
  invariant(typeof newRoot.url === 'string' && newRoot.url.length > 0);
439
434
  // TODO(dmaretskyi): Unify epoch construction.
440
435
  epoch = {
@@ -450,7 +445,7 @@ export class DataSpace {
450
445
  log.info('Fragmenting');
451
446
 
452
447
  const currentRootUrl = this._automergeSpaceState.rootUrl;
453
- const rootHandle = this._automergeHost.repo.find<SpaceDoc>(currentRootUrl as any);
448
+ const rootHandle = this._echoHost.automergeRepo.find<SpaceDoc>(currentRootUrl as any);
454
449
  await cancelWithContext(this._ctx, asyncTimeout(rootHandle.whenReady(), 10_000));
455
450
 
456
451
  // Find properties object.
@@ -461,11 +456,11 @@ export class DataSpace {
461
456
 
462
457
  // Create a new space doc with the properties object.
463
458
  const newSpaceDoc: SpaceDoc = { ...rootHandle.docSync(), objects: Object.fromEntries([properties]) };
464
- const newRoot = this._automergeHost.repo.create(newSpaceDoc);
459
+ const newRoot = this._echoHost.automergeRepo.create(newSpaceDoc);
465
460
  invariant(typeof newRoot.url === 'string' && newRoot.url.length > 0);
466
461
 
467
462
  // Create new automerge documents for all objects.
468
- const docLoader = new AutomergeDocumentLoaderImpl(this.key, this._automergeHost.repo);
463
+ const docLoader = new AutomergeDocumentLoaderImpl(this.key, this._echoHost.automergeRepo);
469
464
  await docLoader.loadSpaceRootDocHandle(this._ctx, { rootUrl: newRoot.url });
470
465
 
471
466
  otherObjects.forEach(([key, value]) => {
@@ -14,6 +14,7 @@ export const createLevel = async (config: Runtime.Client.Storage) => {
14
14
  const persistent = isPersistent(config);
15
15
  const storagePath = persistent ? path.join(getRootPath(config), 'level') : `/tmp/dxos-${PublicKey.random().toHex()}`;
16
16
  const level = new Level<string, string>(storagePath);
17
+ // TODO(dmaretskyi): This function shouldn't call open - .
17
18
  await level.open();
18
19
  return level;
19
20
  };
@@ -60,146 +60,149 @@ export const performInvitation = ({
60
60
  const guestComplete = new Trigger<Result>();
61
61
  const authCode = new Trigger<string>();
62
62
 
63
- const hostObservable = createInvitation(host, options);
64
- hostObservable.subscribe(
65
- async (hostInvitation: Invitation) => {
66
- switch (hostInvitation.state) {
67
- case Invitation.State.CONNECTING: {
68
- if (hooks?.host?.onConnecting?.(hostObservable)) {
69
- break;
70
- }
71
- const guestObservable = acceptInvitation(guest, hostInvitation, guestDeviceProfile);
72
- guestObservable.subscribe(
73
- async (guestInvitation: Invitation) => {
74
- switch (guestInvitation.state) {
75
- case Invitation.State.CONNECTING: {
76
- if (hooks?.guest?.onConnecting?.(guestObservable)) {
63
+ void createInvitation(host, options).then((hostObservable) => {
64
+ hostObservable.subscribe(
65
+ async (hostInvitation: Invitation) => {
66
+ switch (hostInvitation.state) {
67
+ case Invitation.State.CONNECTING: {
68
+ if (hooks?.host?.onConnecting?.(hostObservable)) {
69
+ break;
70
+ }
71
+ const guestObservable = acceptInvitation(guest, hostInvitation, guestDeviceProfile);
72
+ guestObservable.subscribe(
73
+ async (guestInvitation: Invitation) => {
74
+ switch (guestInvitation.state) {
75
+ case Invitation.State.CONNECTING: {
76
+ if (hooks?.guest?.onConnecting?.(guestObservable)) {
77
+ break;
78
+ }
79
+ invariant(hostInvitation.swarmKey!.equals(guestInvitation.swarmKey!));
77
80
  break;
78
81
  }
79
- invariant(hostInvitation.swarmKey!.equals(guestInvitation.swarmKey!));
80
- break;
81
- }
82
82
 
83
- case Invitation.State.CONNECTED: {
84
- hooks?.guest?.onConnected?.(guestObservable);
85
- break;
86
- }
83
+ case Invitation.State.CONNECTED: {
84
+ hooks?.guest?.onConnected?.(guestObservable);
85
+ break;
86
+ }
87
87
 
88
- case Invitation.State.READY_FOR_AUTHENTICATION: {
89
- if (hooks?.guest?.onReady?.(guestObservable)) {
88
+ case Invitation.State.READY_FOR_AUTHENTICATION: {
89
+ if (hooks?.guest?.onReady?.(guestObservable)) {
90
+ break;
91
+ }
92
+ await guestObservable.authenticate(await authCode.wait());
90
93
  break;
91
94
  }
92
- await guestObservable.authenticate(await authCode.wait());
93
- break;
94
- }
95
95
 
96
- case Invitation.State.AUTHENTICATING: {
97
- hooks?.guest?.onAuthenticating?.(guestObservable);
98
- break;
99
- }
96
+ case Invitation.State.AUTHENTICATING: {
97
+ hooks?.guest?.onAuthenticating?.(guestObservable);
98
+ break;
99
+ }
100
100
 
101
- case Invitation.State.SUCCESS: {
102
- if (hooks?.guest?.onSuccess?.(guestObservable)) {
101
+ case Invitation.State.SUCCESS: {
102
+ if (hooks?.guest?.onSuccess?.(guestObservable)) {
103
+ break;
104
+ }
105
+ guestComplete.wake({ invitation: guestInvitation });
103
106
  break;
104
107
  }
105
- guestComplete.wake({ invitation: guestInvitation });
106
- break;
107
- }
108
108
 
109
- case Invitation.State.CANCELLED: {
110
- if (hooks?.guest?.onCancelled?.(guestObservable)) {
109
+ case Invitation.State.CANCELLED: {
110
+ if (hooks?.guest?.onCancelled?.(guestObservable)) {
111
+ break;
112
+ }
113
+ guestComplete.wake({ invitation: guestInvitation });
111
114
  break;
112
115
  }
113
- guestComplete.wake({ invitation: guestInvitation });
114
- break;
115
- }
116
116
 
117
- case Invitation.State.TIMEOUT: {
118
- if (hooks?.guest?.onTimeout?.(guestObservable)) {
119
- return;
117
+ case Invitation.State.TIMEOUT: {
118
+ if (hooks?.guest?.onTimeout?.(guestObservable)) {
119
+ return;
120
+ }
121
+ guestComplete.wake({ invitation: guestInvitation });
120
122
  }
121
- guestComplete.wake({ invitation: guestInvitation });
122
123
  }
123
- }
124
- },
125
- (error: Error) => {
126
- if (hooks?.guest?.onError?.(guestObservable)) {
127
- return;
128
- }
129
- guestComplete.wake({ error });
130
- },
131
- );
132
- break;
133
- }
134
-
135
- case Invitation.State.CONNECTED: {
136
- hooks?.host?.onConnected?.(hostObservable);
137
- break;
138
- }
124
+ },
125
+ (error: Error) => {
126
+ if (hooks?.guest?.onError?.(guestObservable)) {
127
+ return;
128
+ }
129
+ guestComplete.wake({ error });
130
+ },
131
+ );
132
+ break;
133
+ }
139
134
 
140
- case Invitation.State.READY_FOR_AUTHENTICATION: {
141
- if (hooks?.host?.onReady?.(hostObservable)) {
135
+ case Invitation.State.CONNECTED: {
136
+ hooks?.host?.onConnected?.(hostObservable);
142
137
  break;
143
138
  }
144
- if (hostInvitation.authCode) {
145
- authCode.wake(hostInvitation.authCode);
139
+
140
+ case Invitation.State.READY_FOR_AUTHENTICATION: {
141
+ if (hooks?.host?.onReady?.(hostObservable)) {
142
+ break;
143
+ }
144
+ if (hostInvitation.authCode) {
145
+ authCode.wake(hostInvitation.authCode);
146
+ }
147
+ break;
146
148
  }
147
- break;
148
- }
149
149
 
150
- case Invitation.State.AUTHENTICATING: {
151
- hooks?.host?.onAuthenticating?.(hostObservable);
152
- break;
153
- }
150
+ case Invitation.State.AUTHENTICATING: {
151
+ hooks?.host?.onAuthenticating?.(hostObservable);
152
+ break;
153
+ }
154
154
 
155
- case Invitation.State.SUCCESS: {
156
- if (hooks?.host?.onSuccess?.(hostObservable)) {
155
+ case Invitation.State.SUCCESS: {
156
+ if (hooks?.host?.onSuccess?.(hostObservable)) {
157
+ break;
158
+ }
159
+ hostComplete.wake({ invitation: hostInvitation });
157
160
  break;
158
161
  }
159
- hostComplete.wake({ invitation: hostInvitation });
160
- break;
161
- }
162
162
 
163
- case Invitation.State.CANCELLED: {
164
- if (hooks?.host?.onCancelled?.(hostObservable)) {
163
+ case Invitation.State.CANCELLED: {
164
+ if (hooks?.host?.onCancelled?.(hostObservable)) {
165
+ break;
166
+ }
167
+ hostComplete.wake({ invitation: hostInvitation });
165
168
  break;
166
169
  }
167
- hostComplete.wake({ invitation: hostInvitation });
168
- break;
169
- }
170
170
 
171
- case Invitation.State.TIMEOUT: {
172
- if (hooks?.host?.onTimeout?.(hostObservable)) {
171
+ case Invitation.State.TIMEOUT: {
172
+ if (hooks?.host?.onTimeout?.(hostObservable)) {
173
+ break;
174
+ }
175
+ hostComplete.wake({ invitation: hostInvitation });
173
176
  break;
174
177
  }
175
- hostComplete.wake({ invitation: hostInvitation });
176
- break;
177
178
  }
178
- }
179
- },
180
- (error: Error) => {
181
- if (hooks?.host?.onError?.(hostObservable)) {
182
- return;
183
- }
184
- hostComplete.wake({ error });
185
- },
186
- );
179
+ },
180
+ (error: Error) => {
181
+ if (hooks?.host?.onError?.(hostObservable)) {
182
+ return;
183
+ }
184
+ hostComplete.wake({ error });
185
+ },
186
+ );
187
+ });
187
188
 
188
189
  return [hostComplete.wait(), guestComplete.wait()];
189
190
  };
190
191
 
191
- const createInvitation = (
192
+ const createInvitation = async (
192
193
  host: ServiceContext | InvitationHost,
193
194
  options?: Partial<Invitation>,
194
- ): CancellableInvitation => {
195
+ ): Promise<CancellableInvitation> => {
195
196
  options ??= {
196
197
  authMethod: Invitation.AuthMethod.NONE,
197
198
  ...(options ?? {}),
198
199
  };
199
200
 
200
201
  if (host instanceof ServiceContext) {
201
- const hostHandler = host.getInvitationHandler({ kind: Invitation.Kind.SPACE, ...options });
202
- return host.invitations.createInvitation(hostHandler, options);
202
+ return host.invitationsManager.createInvitation({
203
+ kind: Invitation.Kind.SPACE,
204
+ ...options,
205
+ });
203
206
  }
204
207
 
205
208
  return host.share(options);
@@ -6,22 +6,18 @@ import { type Config } from '@dxos/config';
6
6
  import { Context } from '@dxos/context';
7
7
  import { createCredentialSignerWithChain, CredentialGenerator } from '@dxos/credentials';
8
8
  import { failUndefined } from '@dxos/debug';
9
- import {
10
- AutomergeHost,
11
- MetadataStore,
12
- type LevelDB,
13
- SnapshotStore,
14
- SpaceManager,
15
- valueEncoding,
16
- } from '@dxos/echo-pipeline';
9
+ import { EchoHost } from '@dxos/echo-db';
10
+ import { MetadataStore, type LevelDB, SnapshotStore, SpaceManager, valueEncoding } from '@dxos/echo-pipeline';
17
11
  import { createTestLevel } from '@dxos/echo-pipeline/testing';
18
12
  import { FeedFactory, FeedStore } from '@dxos/feed-store';
19
13
  import { Keyring } from '@dxos/keyring';
20
14
  import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
21
15
  import { MemoryTransportFactory, NetworkManager } from '@dxos/network-manager';
16
+ import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
22
17
  import { createStorage, StorageType, type Storage } from '@dxos/random-access-storage';
23
18
  import { BlobStore } from '@dxos/teleport-extension-object-sync';
24
19
 
20
+ import { InvitationsHandler, InvitationsManager, SpaceInvitationProtocol } from '../invitations';
25
21
  import { ClientServicesHost, ServiceContext } from '../services';
26
22
  import { DataSpaceManager, type SigningContext } from '../spaces';
27
23
 
@@ -103,7 +99,8 @@ export type TestPeerProps = {
103
99
  snapshotStore?: SnapshotStore;
104
100
  signingContext?: SigningContext;
105
101
  blobStore?: BlobStore;
106
- automergeHost?: AutomergeHost;
102
+ echoHost?: EchoHost;
103
+ invitationsManager?: InvitationsManager;
107
104
  };
108
105
 
109
106
  export class TestPeer {
@@ -175,20 +172,36 @@ export class TestPeer {
175
172
  return this._props.signingContext ?? failUndefined();
176
173
  }
177
174
 
178
- get automergeHost() {
179
- return (this._props.automergeHost ??= new AutomergeHost({
180
- db: this.level.sublevel('automerge'),
175
+ get echoHost() {
176
+ return (this._props.echoHost ??= new EchoHost({
177
+ kv: this.level,
178
+ storage: this.storage,
181
179
  }));
182
180
  }
183
181
 
184
- get dataSpaceManager() {
182
+ get dataSpaceManager(): DataSpaceManager {
185
183
  return (this._props.dataSpaceManager ??= new DataSpaceManager(
186
184
  this.spaceManager,
187
185
  this.metadataStore,
188
186
  this.keyring,
189
187
  this.identity,
190
188
  this.feedStore,
191
- this.automergeHost,
189
+ this.echoHost,
190
+ this.invitationsManager,
191
+ ));
192
+ }
193
+
194
+ get invitationsManager() {
195
+ return (this._props.invitationsManager ??= new InvitationsManager(
196
+ new InvitationsHandler(this.networkManager),
197
+ (invitation) => {
198
+ if (invitation.kind === Invitation.Kind.SPACE) {
199
+ return new SpaceInvitationProtocol(this.dataSpaceManager, this.identity!, this.keyring, invitation.spaceKey!);
200
+ } else {
201
+ throw new Error('not implemented');
202
+ }
203
+ },
204
+ this.metadataStore,
192
205
  ));
193
206
  }
194
207
 
@@ -120,7 +120,9 @@ export class WorkerRuntime {
120
120
  this._sessions.delete(session);
121
121
  if (this._sessions.size === 0) {
122
122
  // Terminate the worker when all sessions are closed.
123
- self.close();
123
+ if (globalThis.self) {
124
+ self.close();
125
+ }
124
126
  } else {
125
127
  this._reconnectWebrtc();
126
128
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const DXOS_VERSION = "0.4.10-main.c75170d";
1
+ export const DXOS_VERSION = "0.4.10-main.c8e5c39";