@dxos/client-services 0.4.10-main.4c8c73c → 0.4.10-main.4d48ccf

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 (53) hide show
  1. package/dist/lib/browser/{chunk-F4LOSBRJ.mjs → chunk-VQZR7ZZQ.mjs} +356 -288
  2. package/dist/lib/browser/chunk-VQZR7ZZQ.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1 -1
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/packlets/testing/index.mjs +125 -112
  6. package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
  7. package/dist/lib/node/{chunk-IPO3WM44.cjs → chunk-G4BQZF43.cjs} +448 -380
  8. package/dist/lib/node/chunk-G4BQZF43.cjs.map +7 -0
  9. package/dist/lib/node/index.cjs +43 -43
  10. package/dist/lib/node/meta.json +1 -1
  11. package/dist/lib/node/packlets/testing/index.cjs +125 -115
  12. package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
  13. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  14. package/dist/types/src/packlets/indexing/util.d.ts +0 -5
  15. package/dist/types/src/packlets/indexing/util.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.map +1 -1
  28. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +5 -1
  29. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  30. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  31. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  32. package/dist/types/src/packlets/testing/test-builder.d.ts +3 -0
  33. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  34. package/dist/types/src/version.d.ts +1 -1
  35. package/package.json +34 -34
  36. package/src/packlets/identity/identity-manager.ts +1 -0
  37. package/src/packlets/identity/identity.test.ts +3 -0
  38. package/src/packlets/indexing/util.ts +1 -65
  39. package/src/packlets/invitations/device-invitation-protocol.ts +6 -1
  40. package/src/packlets/invitations/invitation-protocol.ts +7 -1
  41. package/src/packlets/invitations/invitations-handler.ts +10 -71
  42. package/src/packlets/invitations/invitations-manager.ts +114 -40
  43. package/src/packlets/invitations/invitations-service.ts +4 -2
  44. package/src/packlets/invitations/space-invitation-protocol.ts +45 -3
  45. package/src/packlets/services/automerge-host.test.ts +1 -1
  46. package/src/packlets/services/service-context.ts +3 -2
  47. package/src/packlets/spaces/data-space-manager.ts +48 -2
  48. package/src/packlets/spaces/data-space.ts +1 -1
  49. package/src/packlets/testing/invitation-utils.ts +100 -97
  50. package/src/packlets/testing/test-builder.ts +19 -1
  51. package/src/version.ts +1 -1
  52. package/dist/lib/browser/chunk-F4LOSBRJ.mjs.map +0 -7
  53. package/dist/lib/node/chunk-IPO3WM44.cjs.map +0 -7
@@ -2,20 +2,27 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Event } from '@dxos/async';
6
- import type { AuthenticatingInvitation, CancellableInvitation } from '@dxos/client-protocol';
7
- import { type Context } from '@dxos/context';
5
+ import { Event, PushStream } from '@dxos/async';
6
+ import {
7
+ type AuthenticatingInvitation,
8
+ AUTHENTICATION_CODE_LENGTH,
9
+ CancellableInvitation,
10
+ INVITATION_TIMEOUT,
11
+ } from '@dxos/client-protocol';
12
+ import { Context } from '@dxos/context';
13
+ import { generatePasscode } from '@dxos/credentials';
8
14
  import { hasInvitationExpired, type MetadataStore } from '@dxos/echo-pipeline';
9
15
  import { invariant } from '@dxos/invariant';
16
+ import { PublicKey } from '@dxos/keys';
10
17
  import { log } from '@dxos/log';
11
18
  import {
12
19
  type AcceptInvitationRequest,
13
- type Invitation,
14
20
  type AuthenticationRequest,
21
+ Invitation,
15
22
  } from '@dxos/protocols/proto/dxos/client/services';
16
23
 
17
24
  import type { InvitationProtocol } from './invitation-protocol';
18
- import type { InvitationsHandler } from './invitations-handler';
25
+ import { createAdmissionKeypair, type InvitationsHandler } from './invitations-handler';
19
26
 
20
27
  /**
21
28
  * Entry point for creating and accepting invitations, keeps track of existing invitation set and
@@ -36,36 +43,44 @@ export class InvitationsManager {
36
43
 
37
44
  constructor(
38
45
  private readonly _invitationsHandler: InvitationsHandler,
39
- private readonly _getHandler: (invitation: Invitation) => InvitationProtocol,
46
+ private readonly _getHandler: (invitation: Partial<Invitation> & Pick<Invitation, 'kind'>) => InvitationProtocol,
40
47
  private readonly _metadataStore: MetadataStore,
41
48
  ) {}
42
49
 
43
- createInvitation(options: Invitation): CancellableInvitation {
44
- const existingInvitation = this._createInvitations.get(options.invitationId);
45
- if (existingInvitation) {
46
- return existingInvitation;
50
+ async createInvitation(options: Partial<Invitation> & Pick<Invitation, 'kind'>): Promise<CancellableInvitation> {
51
+ if (options.invitationId) {
52
+ const existingInvitation = this._createInvitations.get(options.invitationId);
53
+ if (existingInvitation) {
54
+ return existingInvitation;
55
+ }
47
56
  }
48
57
 
49
58
  const handler = this._getHandler(options);
50
- const invitation = this._invitationsHandler.createInvitation(handler, options);
51
- this._createInvitations.set(invitation.get().invitationId, invitation);
52
- this.invitationCreated.emit(invitation.get());
53
-
54
- const saveInvitationTask = invitation.get().persistent
55
- ? this._safePersistInBackground(invitation)
56
- : Promise.resolve();
59
+ const invitation = this._createInvitation(handler, options);
60
+ const { ctx, stream, observableInvitation } = this._createObservableInvitation(handler, invitation);
57
61
 
62
+ this._createInvitations.set(invitation.invitationId, observableInvitation);
63
+ this.invitationCreated.emit(invitation);
58
64
  // onComplete is called on cancel, expiration, or redemption of a single-use invitation
59
- this._onInvitationComplete(invitation, async () => {
60
- this._createInvitations.delete(invitation.get().invitationId);
61
- this.removedCreated.emit(invitation.get());
62
- if (invitation.get().persistent) {
63
- await saveInvitationTask;
64
- await this._safeDeleteInvitation(invitation.get());
65
+ this._onInvitationComplete(observableInvitation, async () => {
66
+ this._createInvitations.delete(observableInvitation.get().invitationId);
67
+ this.removedCreated.emit(observableInvitation.get());
68
+ if (observableInvitation.get().persistent) {
69
+ await this._safeDeleteInvitation(observableInvitation.get());
65
70
  }
66
71
  });
67
72
 
68
- return invitation;
73
+ try {
74
+ await this._persistIfRequired(handler, stream, invitation);
75
+ } catch (err) {
76
+ log.catch(err);
77
+ await observableInvitation.cancel();
78
+ return observableInvitation;
79
+ }
80
+
81
+ this._invitationsHandler.handleInvitationFlow(ctx, stream, handler, observableInvitation.get());
82
+
83
+ return observableInvitation;
69
84
  }
70
85
 
71
86
  async loadPersistentInvitations(): Promise<{ invitations: Invitation[] }> {
@@ -78,12 +93,13 @@ export class InvitationsManager {
78
93
  // get saved persistent invitations, filter and remove from storage those that have expired.
79
94
  const freshInvitations = persistentInvitations.filter((invitation) => !hasInvitationExpired(invitation));
80
95
 
81
- const cInvitations = freshInvitations.map((persistentInvitation) => {
96
+ const loadTasks = freshInvitations.map((persistentInvitation) => {
82
97
  invariant(!this._createInvitations.get(persistentInvitation.invitationId), 'invitation already exists');
83
- return this.createInvitation({ ...persistentInvitation, persistent: false }).get();
98
+ return this.createInvitation({ ...persistentInvitation, persistent: false });
84
99
  });
100
+ const cInvitations = await Promise.all(loadTasks);
85
101
 
86
- return { invitations: cInvitations };
102
+ return { invitations: cInvitations.map((invitation) => invitation.get()) };
87
103
  } catch (err) {
88
104
  log.catch(err);
89
105
  return { invitations: [] };
@@ -163,20 +179,78 @@ export class InvitationsManager {
163
179
  }
164
180
  }
165
181
 
166
- private _safePersistInBackground(invitation: CancellableInvitation): Promise<void> {
167
- return new Promise((resolve) => {
168
- setTimeout(async () => {
169
- try {
170
- await this._metadataStore.addInvitation(invitation.get());
171
- this.saved.emit(invitation.get());
172
- } catch (err: any) {
173
- log.catch(err);
174
- await invitation.cancel();
175
- } finally {
176
- resolve();
177
- }
178
- });
182
+ private _createInvitation(protocol: InvitationProtocol, options?: Partial<Invitation>): Invitation {
183
+ const {
184
+ invitationId = PublicKey.random().toHex(),
185
+ type = Invitation.Type.INTERACTIVE,
186
+ authMethod = Invitation.AuthMethod.SHARED_SECRET,
187
+ state = Invitation.State.INIT,
188
+ timeout = INVITATION_TIMEOUT,
189
+ swarmKey = PublicKey.random(),
190
+ persistent = options?.authMethod !== Invitation.AuthMethod.KNOWN_PUBLIC_KEY, // default no not storing keypairs
191
+ created = new Date(),
192
+ guestKeypair = undefined,
193
+ lifetime = 86400, // 1 day,
194
+ multiUse = false,
195
+ } = options ?? {};
196
+ const authCode =
197
+ options?.authCode ??
198
+ (authMethod === Invitation.AuthMethod.SHARED_SECRET ? generatePasscode(AUTHENTICATION_CODE_LENGTH) : undefined);
199
+
200
+ return {
201
+ invitationId,
202
+ type,
203
+ authMethod,
204
+ state,
205
+ swarmKey,
206
+ authCode,
207
+ timeout,
208
+ persistent: persistent && type !== Invitation.Type.DELEGATED, // delegated invitations are persisted in control feed
209
+ guestKeypair:
210
+ guestKeypair ?? (authMethod === Invitation.AuthMethod.KNOWN_PUBLIC_KEY ? createAdmissionKeypair() : undefined),
211
+ created,
212
+ lifetime,
213
+ multiUse,
214
+ delegationCredentialId: options?.delegationCredentialId,
215
+ ...protocol.getInvitationContext(),
216
+ } satisfies Invitation;
217
+ }
218
+
219
+ private _createObservableInvitation(handler: InvitationProtocol, invitation: Invitation) {
220
+ const stream = new PushStream<Invitation>();
221
+ const ctx = new Context({
222
+ onError: (err) => {
223
+ stream.error(err);
224
+ void ctx.dispose();
225
+ },
179
226
  });
227
+ ctx.onDispose(() => {
228
+ log('complete', { ...handler.toJSON() });
229
+ stream.complete();
230
+ });
231
+ const observableInvitation = new CancellableInvitation({
232
+ initialInvitation: invitation,
233
+ subscriber: stream.observable,
234
+ onCancel: async () => {
235
+ stream.next({ ...invitation, state: Invitation.State.CANCELLED });
236
+ await ctx.dispose();
237
+ },
238
+ });
239
+ return { ctx, stream, observableInvitation };
240
+ }
241
+
242
+ private async _persistIfRequired(
243
+ handler: InvitationProtocol,
244
+ changeStream: PushStream<Invitation>,
245
+ invitation: Invitation,
246
+ ): Promise<void> {
247
+ if (invitation.type === Invitation.Type.DELEGATED && invitation.delegationCredentialId == null) {
248
+ const delegationCredentialId = await handler.delegate(invitation);
249
+ changeStream.next({ ...invitation, delegationCredentialId });
250
+ } else if (invitation.persistent) {
251
+ await this._metadataStore.addInvitation(invitation);
252
+ this.saved.emit(invitation);
253
+ }
180
254
  }
181
255
 
182
256
  private async _safeDeleteInvitation(invitation: Invitation): Promise<void> {
@@ -27,9 +27,11 @@ export class InvitationsServiceImpl implements InvitationsService {
27
27
  }
28
28
 
29
29
  createInvitation(options: Invitation): Stream<Invitation> {
30
- const invitation = this._invitationsManager.createInvitation(options);
31
30
  return new Stream<Invitation>(({ next, close }) => {
32
- invitation.subscribe(next, close, close);
31
+ void this._invitationsManager
32
+ .createInvitation(options)
33
+ .then((invitation) => invitation.subscribe(next, close, close))
34
+ .catch(close);
33
35
  });
34
36
  }
35
37
 
@@ -2,7 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { createAdmissionCredentials, getCredentialAssertion } from '@dxos/credentials';
5
+ import {
6
+ createAdmissionCredentials,
7
+ createDelegatedSpaceInvitationCredential,
8
+ getCredentialAssertion,
9
+ } from '@dxos/credentials';
6
10
  import { writeMessages } from '@dxos/feed-store';
7
11
  import { invariant } from '@dxos/invariant';
8
12
  import { type Keyring } from '@dxos/keyring';
@@ -11,7 +15,7 @@ import { log } from '@dxos/log';
11
15
  import { AlreadyJoinedError } from '@dxos/protocols';
12
16
  import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
13
17
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
14
- import { type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
18
+ import { SpaceMember, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
15
19
  import {
16
20
  type AdmissionRequest,
17
21
  type AdmissionResponse,
@@ -43,7 +47,11 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
43
47
  };
44
48
  }
45
49
 
46
- async admit(request: AdmissionRequest, guestProfile?: ProfileDocument | undefined): Promise<AdmissionResponse> {
50
+ async admit(
51
+ invitation: Invitation,
52
+ request: AdmissionRequest,
53
+ guestProfile?: ProfileDocument | undefined,
54
+ ): Promise<AdmissionResponse> {
47
55
  invariant(this._spaceKey);
48
56
  const space = await this._spaceManager.spaces.get(this._spaceKey);
49
57
  invariant(space);
@@ -59,6 +67,7 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
59
67
  space.key,
60
68
  space.inner.genesisFeedKey,
61
69
  guestProfile,
70
+ invitation.delegationCredentialId,
62
71
  );
63
72
 
64
73
  // TODO(dmaretskyi): Refactor.
@@ -76,6 +85,39 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
76
85
  };
77
86
  }
78
87
 
88
+ async delegate(invitation: Invitation): Promise<PublicKey> {
89
+ invariant(this._spaceKey);
90
+ const space = await this._spaceManager.spaces.get(this._spaceKey);
91
+ invariant(space);
92
+ if (invitation.authMethod === Invitation.AuthMethod.KNOWN_PUBLIC_KEY) {
93
+ invariant(invitation.guestKeypair?.publicKey);
94
+ }
95
+
96
+ log('writing delegate space invitation', { host: this._signingContext.deviceKey, id: invitation.invitationId });
97
+ const credential = await createDelegatedSpaceInvitationCredential(
98
+ this._signingContext.credentialSigner,
99
+ space.key,
100
+ {
101
+ invitationId: invitation.invitationId,
102
+ authMethod: invitation.authMethod,
103
+ swarmKey: invitation.swarmKey,
104
+ role: SpaceMember.Role.ADMIN,
105
+ expiresOn: invitation.lifetime
106
+ ? new Date((invitation.created?.getTime() ?? Date.now()) + invitation.lifetime)
107
+ : undefined,
108
+ multiUse: invitation.multiUse ?? false,
109
+ guestKey:
110
+ invitation.authMethod === Invitation.AuthMethod.KNOWN_PUBLIC_KEY
111
+ ? invitation.guestKeypair!.publicKey
112
+ : undefined,
113
+ },
114
+ );
115
+
116
+ invariant(credential.credential);
117
+ await writeMessages(space.inner.controlPipeline.writer, [credential]);
118
+ return credential.credential.credential.id!;
119
+ }
120
+
79
121
  checkInvitation(invitation: Partial<Invitation>) {
80
122
  if (invitation.spaceKey && this._spaceManager.spaces.has(invitation.spaceKey)) {
81
123
  return new AlreadyJoinedError('Already joined space.');
@@ -5,9 +5,9 @@
5
5
  import { expect } from 'chai';
6
6
 
7
7
  import { asyncTimeout, sleep } from '@dxos/async';
8
+ import { AutomergeContext } from '@dxos/echo-db';
8
9
  import { AutomergeHost, DataServiceImpl } from '@dxos/echo-pipeline';
9
10
  import { createTestLevel } from '@dxos/echo-pipeline/testing';
10
- import { AutomergeContext } from '@dxos/echo-schema';
11
11
  import { afterTest, describe, test } from '@dxos/test';
12
12
 
13
13
  describe('AutomergeHost', () => {
@@ -32,7 +32,7 @@ import {
32
32
  type IdentityManagerRuntimeParams,
33
33
  type JoinIdentityParams,
34
34
  } from '../identity';
35
- import { createDocumentsIterator, createSelectedDocumentsIterator } from '../indexing';
35
+ import { createSelectedDocumentsIterator } from '../indexing';
36
36
  import {
37
37
  DeviceInvitationProtocol,
38
38
  InvitationsHandler,
@@ -131,10 +131,10 @@ export class ServiceContext extends Resource {
131
131
  });
132
132
 
133
133
  this.indexer = new Indexer({
134
+ db: this.level,
134
135
  indexStore: new IndexStore({ db: level.sublevel('index-storage') }),
135
136
  metadataStore: this.indexMetadata,
136
137
  loadDocuments: createSelectedDocumentsIterator(this.automergeHost),
137
- getAllDocuments: createDocumentsIterator(this.automergeHost),
138
138
  });
139
139
 
140
140
  this.invitations = new InvitationsHandler(this.networkManager);
@@ -256,6 +256,7 @@ export class ServiceContext extends Resource {
256
256
  signingContext,
257
257
  this.feedStore,
258
258
  this.automergeHost,
259
+ this.invitationsManager,
259
260
  this._runtimeParams as DataSpaceManagerRuntimeParams,
260
261
  );
261
262
  await this.dataSpaceManager.open();
@@ -4,7 +4,7 @@
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';
7
+ import { getCredentialAssertion, type CredentialSigner, type DelegateInvitationCredential } from '@dxos/credentials';
8
8
  import { type AutomergeHost, type MetadataStore, type Space, type SpaceManager } from '@dxos/echo-pipeline';
9
9
  import { type FeedStore } from '@dxos/feed-store';
10
10
  import { invariant } from '@dxos/invariant';
@@ -12,10 +12,11 @@ import { type Keyring } from '@dxos/keyring';
12
12
  import { PublicKey } from '@dxos/keys';
13
13
  import { log } from '@dxos/log';
14
14
  import { trace } from '@dxos/protocols';
15
- import { SpaceState } from '@dxos/protocols/proto/dxos/client/services';
15
+ import { Invitation, SpaceState } from '@dxos/protocols/proto/dxos/client/services';
16
16
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
17
17
  import { type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
18
18
  import { type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
19
+ import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
19
20
  import { Gossip, Presence } from '@dxos/teleport-extension-gossip';
20
21
  import { type Timeframe } from '@dxos/timeframe';
21
22
  import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
@@ -23,6 +24,7 @@ import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
23
24
  import { DataSpace } from './data-space';
24
25
  import { spaceGenesis } from './genesis';
25
26
  import { createAuthProvider } from '../identity';
27
+ import { type InvitationsManager } from '../invitations';
26
28
 
27
29
  const PRESENCE_ANNOUNCE_INTERVAL = 10_000;
28
30
  const PRESENCE_OFFLINE_TIMEOUT = 20_000;
@@ -78,6 +80,7 @@ export class DataSpaceManager {
78
80
  private readonly _signingContext: SigningContext,
79
81
  private readonly _feedStore: FeedStore<FeedMessage>,
80
82
  private readonly _automergeHost: AutomergeHost,
83
+ private readonly _invitationsManager: InvitationsManager,
81
84
  params?: DataSpaceManagerRuntimeParams,
82
85
  ) {
83
86
  const {
@@ -247,6 +250,9 @@ export class DataSpaceManager {
247
250
  log.warn('auth failure');
248
251
  },
249
252
  memberKey: this._signingContext.identityKey,
253
+ onDelegatedInvitationStatusChange: (invitation, isActive) => {
254
+ return this._handleInvitationStatusChange(dataSpace, invitation, isActive);
255
+ },
250
256
  });
251
257
  controlFeed && (await space.setControlFeed(controlFeed));
252
258
  dataFeed && (await space.setDataFeed(dataFeed));
@@ -267,6 +273,7 @@ export class DataSpaceManager {
267
273
  afterReady: async () => {
268
274
  log('after space ready', { space: space.key, open: this._isOpen });
269
275
  if (this._isOpen) {
276
+ await this._createDelegatedInvitations(dataSpace, [...space.spaceState.invitations.entries()]);
270
277
  this.updated.emit();
271
278
  }
272
279
  },
@@ -289,4 +296,43 @@ export class DataSpaceManager {
289
296
  this._spaces.set(metadata.key, dataSpace);
290
297
  return dataSpace;
291
298
  }
299
+
300
+ private async _handleInvitationStatusChange(
301
+ dataSpace: DataSpace | undefined,
302
+ delegatedInvitation: DelegateInvitationCredential,
303
+ isActive: boolean,
304
+ ): Promise<void> {
305
+ if (dataSpace?.state !== SpaceState.READY) {
306
+ return;
307
+ }
308
+ if (isActive) {
309
+ await this._createDelegatedInvitations(dataSpace, [
310
+ [delegatedInvitation.credentialId, delegatedInvitation.invitation],
311
+ ]);
312
+ } else {
313
+ await this._invitationsManager.cancelInvitation(delegatedInvitation.invitation);
314
+ }
315
+ }
316
+
317
+ private async _createDelegatedInvitations(
318
+ space: DataSpace,
319
+ invitations: Array<[PublicKey, DelegateSpaceInvitation]>,
320
+ ): Promise<void> {
321
+ const tasks = invitations.map(([credentialId, invitation]) => {
322
+ return this._invitationsManager.createInvitation({
323
+ type: Invitation.Type.DELEGATED,
324
+ kind: Invitation.Kind.SPACE,
325
+ spaceKey: space.key,
326
+ authMethod: invitation.authMethod,
327
+ invitationId: invitation.invitationId,
328
+ swarmKey: invitation.swarmKey,
329
+ guestKeypair: invitation.guestKey ? { publicKey: invitation.guestKey } : undefined,
330
+ lifetime: invitation.expiresOn ? invitation.expiresOn.getTime() - Date.now() : undefined,
331
+ multiUse: invitation.multiUse,
332
+ delegationCredentialId: credentialId,
333
+ persistent: false,
334
+ });
335
+ });
336
+ await Promise.all(tasks);
337
+ }
292
338
  }
@@ -6,7 +6,6 @@ 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
9
  import {
11
10
  type MetadataStore,
12
11
  type Space,
@@ -15,6 +14,7 @@ import {
15
14
  type SpaceDoc,
16
15
  } from '@dxos/echo-pipeline';
17
16
  import { AutomergeDocumentLoaderImpl } from '@dxos/echo-pipeline';
17
+ import { TYPE_PROPERTIES } from '@dxos/echo-schema';
18
18
  import { type FeedStore } from '@dxos/feed-store';
19
19
  import { failedInvariant, invariant } from '@dxos/invariant';
20
20
  import { type Keyring } from '@dxos/keyring';