@dxos/client-services 0.4.10-main.e6ba7bc → 0.4.10-main.e96274c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/{chunk-NE6FWMYP.mjs → chunk-UVEPMM4Z.mjs} +287 -355
- package/dist/lib/browser/chunk-UVEPMM4Z.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +112 -125
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-3EKN7HG6.cjs → chunk-GZWBW7OC.cjs} +377 -445
- package/dist/lib/node/chunk-GZWBW7OC.cjs.map +7 -0
- package/dist/lib/node/index.cjs +43 -43
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +115 -125
- package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/indexing/util.d.ts +5 -0
- package/dist/types/src/packlets/indexing/util.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +1 -3
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +1 -6
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +2 -4
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts +7 -9
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +1 -2
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +1 -5
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +0 -3
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/package.json +34 -34
- package/src/packlets/identity/identity-manager.ts +0 -1
- package/src/packlets/identity/identity.test.ts +0 -3
- package/src/packlets/indexing/util.ts +65 -1
- package/src/packlets/invitations/device-invitation-protocol.ts +1 -6
- package/src/packlets/invitations/invitation-protocol.ts +1 -7
- package/src/packlets/invitations/invitations-handler.ts +71 -10
- package/src/packlets/invitations/invitations-manager.ts +40 -114
- package/src/packlets/invitations/invitations-service.ts +2 -4
- package/src/packlets/invitations/space-invitation-protocol.ts +3 -45
- package/src/packlets/services/service-context.ts +2 -3
- package/src/packlets/spaces/data-space-manager.ts +2 -48
- package/src/packlets/testing/invitation-utils.ts +97 -100
- package/src/packlets/testing/test-builder.ts +1 -19
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-NE6FWMYP.mjs.map +0 -7
- package/dist/lib/node/chunk-3EKN7HG6.cjs.map +0 -7
|
@@ -2,27 +2,20 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Event
|
|
6
|
-
import {
|
|
7
|
-
|
|
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';
|
|
5
|
+
import { Event } from '@dxos/async';
|
|
6
|
+
import type { AuthenticatingInvitation, CancellableInvitation } from '@dxos/client-protocol';
|
|
7
|
+
import { type Context } from '@dxos/context';
|
|
14
8
|
import { hasInvitationExpired, type MetadataStore } from '@dxos/echo-pipeline';
|
|
15
9
|
import { invariant } from '@dxos/invariant';
|
|
16
|
-
import { PublicKey } from '@dxos/keys';
|
|
17
10
|
import { log } from '@dxos/log';
|
|
18
11
|
import {
|
|
19
12
|
type AcceptInvitationRequest,
|
|
13
|
+
type Invitation,
|
|
20
14
|
type AuthenticationRequest,
|
|
21
|
-
Invitation,
|
|
22
15
|
} from '@dxos/protocols/proto/dxos/client/services';
|
|
23
16
|
|
|
24
17
|
import type { InvitationProtocol } from './invitation-protocol';
|
|
25
|
-
import {
|
|
18
|
+
import type { InvitationsHandler } from './invitations-handler';
|
|
26
19
|
|
|
27
20
|
/**
|
|
28
21
|
* Entry point for creating and accepting invitations, keeps track of existing invitation set and
|
|
@@ -43,44 +36,36 @@ export class InvitationsManager {
|
|
|
43
36
|
|
|
44
37
|
constructor(
|
|
45
38
|
private readonly _invitationsHandler: InvitationsHandler,
|
|
46
|
-
private readonly _getHandler: (invitation:
|
|
39
|
+
private readonly _getHandler: (invitation: Invitation) => InvitationProtocol,
|
|
47
40
|
private readonly _metadataStore: MetadataStore,
|
|
48
41
|
) {}
|
|
49
42
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return existingInvitation;
|
|
55
|
-
}
|
|
43
|
+
createInvitation(options: Invitation): CancellableInvitation {
|
|
44
|
+
const existingInvitation = this._createInvitations.get(options.invitationId);
|
|
45
|
+
if (existingInvitation) {
|
|
46
|
+
return existingInvitation;
|
|
56
47
|
}
|
|
57
48
|
|
|
58
49
|
const handler = this._getHandler(options);
|
|
59
|
-
const invitation = this.
|
|
60
|
-
|
|
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();
|
|
61
57
|
|
|
62
|
-
this._createInvitations.set(invitation.invitationId, observableInvitation);
|
|
63
|
-
this.invitationCreated.emit(invitation);
|
|
64
58
|
// onComplete is called on cancel, expiration, or redemption of a single-use invitation
|
|
65
|
-
this._onInvitationComplete(
|
|
66
|
-
this._createInvitations.delete(
|
|
67
|
-
this.removedCreated.emit(
|
|
68
|
-
if (
|
|
69
|
-
await
|
|
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());
|
|
70
65
|
}
|
|
71
66
|
});
|
|
72
67
|
|
|
73
|
-
|
|
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;
|
|
68
|
+
return invitation;
|
|
84
69
|
}
|
|
85
70
|
|
|
86
71
|
async loadPersistentInvitations(): Promise<{ invitations: Invitation[] }> {
|
|
@@ -93,13 +78,12 @@ export class InvitationsManager {
|
|
|
93
78
|
// get saved persistent invitations, filter and remove from storage those that have expired.
|
|
94
79
|
const freshInvitations = persistentInvitations.filter((invitation) => !hasInvitationExpired(invitation));
|
|
95
80
|
|
|
96
|
-
const
|
|
81
|
+
const cInvitations = freshInvitations.map((persistentInvitation) => {
|
|
97
82
|
invariant(!this._createInvitations.get(persistentInvitation.invitationId), 'invitation already exists');
|
|
98
|
-
return this.createInvitation({ ...persistentInvitation, persistent: false });
|
|
83
|
+
return this.createInvitation({ ...persistentInvitation, persistent: false }).get();
|
|
99
84
|
});
|
|
100
|
-
const cInvitations = await Promise.all(loadTasks);
|
|
101
85
|
|
|
102
|
-
return { invitations: cInvitations
|
|
86
|
+
return { invitations: cInvitations };
|
|
103
87
|
} catch (err) {
|
|
104
88
|
log.catch(err);
|
|
105
89
|
return { invitations: [] };
|
|
@@ -179,78 +163,20 @@ export class InvitationsManager {
|
|
|
179
163
|
}
|
|
180
164
|
}
|
|
181
165
|
|
|
182
|
-
private
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
+
});
|
|
238
179
|
});
|
|
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
|
-
}
|
|
254
180
|
}
|
|
255
181
|
|
|
256
182
|
private async _safeDeleteInvitation(invitation: Invitation): Promise<void> {
|
|
@@ -27,11 +27,9 @@ export class InvitationsServiceImpl implements InvitationsService {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
createInvitation(options: Invitation): Stream<Invitation> {
|
|
30
|
+
const invitation = this._invitationsManager.createInvitation(options);
|
|
30
31
|
return new Stream<Invitation>(({ next, close }) => {
|
|
31
|
-
|
|
32
|
-
.createInvitation(options)
|
|
33
|
-
.then((invitation) => invitation.subscribe(next, close, close))
|
|
34
|
-
.catch(close);
|
|
32
|
+
invitation.subscribe(next, close, close);
|
|
35
33
|
});
|
|
36
34
|
}
|
|
37
35
|
|
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
createAdmissionCredentials,
|
|
7
|
-
createDelegatedSpaceInvitationCredential,
|
|
8
|
-
getCredentialAssertion,
|
|
9
|
-
} from '@dxos/credentials';
|
|
5
|
+
import { createAdmissionCredentials, getCredentialAssertion } from '@dxos/credentials';
|
|
10
6
|
import { writeMessages } from '@dxos/feed-store';
|
|
11
7
|
import { invariant } from '@dxos/invariant';
|
|
12
8
|
import { type Keyring } from '@dxos/keyring';
|
|
@@ -15,7 +11,7 @@ import { log } from '@dxos/log';
|
|
|
15
11
|
import { AlreadyJoinedError } from '@dxos/protocols';
|
|
16
12
|
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
17
13
|
import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
18
|
-
import {
|
|
14
|
+
import { type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
19
15
|
import {
|
|
20
16
|
type AdmissionRequest,
|
|
21
17
|
type AdmissionResponse,
|
|
@@ -47,11 +43,7 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
|
|
|
47
43
|
};
|
|
48
44
|
}
|
|
49
45
|
|
|
50
|
-
async admit(
|
|
51
|
-
invitation: Invitation,
|
|
52
|
-
request: AdmissionRequest,
|
|
53
|
-
guestProfile?: ProfileDocument | undefined,
|
|
54
|
-
): Promise<AdmissionResponse> {
|
|
46
|
+
async admit(request: AdmissionRequest, guestProfile?: ProfileDocument | undefined): Promise<AdmissionResponse> {
|
|
55
47
|
invariant(this._spaceKey);
|
|
56
48
|
const space = await this._spaceManager.spaces.get(this._spaceKey);
|
|
57
49
|
invariant(space);
|
|
@@ -67,7 +59,6 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
|
|
|
67
59
|
space.key,
|
|
68
60
|
space.inner.genesisFeedKey,
|
|
69
61
|
guestProfile,
|
|
70
|
-
invitation.delegationCredentialId,
|
|
71
62
|
);
|
|
72
63
|
|
|
73
64
|
// TODO(dmaretskyi): Refactor.
|
|
@@ -85,39 +76,6 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
|
|
|
85
76
|
};
|
|
86
77
|
}
|
|
87
78
|
|
|
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
|
-
|
|
121
79
|
checkInvitation(invitation: Partial<Invitation>) {
|
|
122
80
|
if (invitation.spaceKey && this._spaceManager.spaces.has(invitation.spaceKey)) {
|
|
123
81
|
return new AlreadyJoinedError('Already joined space.');
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
type IdentityManagerRuntimeParams,
|
|
33
33
|
type JoinIdentityParams,
|
|
34
34
|
} from '../identity';
|
|
35
|
-
import { createSelectedDocumentsIterator } from '../indexing';
|
|
35
|
+
import { createDocumentsIterator, 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,
|
|
135
134
|
indexStore: new IndexStore({ db: level.sublevel('index-storage') }),
|
|
136
135
|
metadataStore: this.indexMetadata,
|
|
137
136
|
loadDocuments: createSelectedDocumentsIterator(this.automergeHost),
|
|
137
|
+
getAllDocuments: createDocumentsIterator(this.automergeHost),
|
|
138
138
|
});
|
|
139
139
|
|
|
140
140
|
this.invitations = new InvitationsHandler(this.networkManager);
|
|
@@ -256,7 +256,6 @@ export class ServiceContext extends Resource {
|
|
|
256
256
|
signingContext,
|
|
257
257
|
this.feedStore,
|
|
258
258
|
this.automergeHost,
|
|
259
|
-
this.invitationsManager,
|
|
260
259
|
this._runtimeParams as DataSpaceManagerRuntimeParams,
|
|
261
260
|
);
|
|
262
261
|
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
|
|
7
|
+
import { getCredentialAssertion, type CredentialSigner } 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,11 +12,10 @@ 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 {
|
|
15
|
+
import { 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';
|
|
20
19
|
import { Gossip, Presence } from '@dxos/teleport-extension-gossip';
|
|
21
20
|
import { type Timeframe } from '@dxos/timeframe';
|
|
22
21
|
import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
|
|
@@ -24,7 +23,6 @@ import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
|
|
|
24
23
|
import { DataSpace } from './data-space';
|
|
25
24
|
import { spaceGenesis } from './genesis';
|
|
26
25
|
import { createAuthProvider } from '../identity';
|
|
27
|
-
import { type InvitationsManager } from '../invitations';
|
|
28
26
|
|
|
29
27
|
const PRESENCE_ANNOUNCE_INTERVAL = 10_000;
|
|
30
28
|
const PRESENCE_OFFLINE_TIMEOUT = 20_000;
|
|
@@ -80,7 +78,6 @@ export class DataSpaceManager {
|
|
|
80
78
|
private readonly _signingContext: SigningContext,
|
|
81
79
|
private readonly _feedStore: FeedStore<FeedMessage>,
|
|
82
80
|
private readonly _automergeHost: AutomergeHost,
|
|
83
|
-
private readonly _invitationsManager: InvitationsManager,
|
|
84
81
|
params?: DataSpaceManagerRuntimeParams,
|
|
85
82
|
) {
|
|
86
83
|
const {
|
|
@@ -250,9 +247,6 @@ export class DataSpaceManager {
|
|
|
250
247
|
log.warn('auth failure');
|
|
251
248
|
},
|
|
252
249
|
memberKey: this._signingContext.identityKey,
|
|
253
|
-
onDelegatedInvitationStatusChange: (invitation, isActive) => {
|
|
254
|
-
return this._handleInvitationStatusChange(dataSpace, invitation, isActive);
|
|
255
|
-
},
|
|
256
250
|
});
|
|
257
251
|
controlFeed && (await space.setControlFeed(controlFeed));
|
|
258
252
|
dataFeed && (await space.setDataFeed(dataFeed));
|
|
@@ -273,7 +267,6 @@ export class DataSpaceManager {
|
|
|
273
267
|
afterReady: async () => {
|
|
274
268
|
log('after space ready', { space: space.key, open: this._isOpen });
|
|
275
269
|
if (this._isOpen) {
|
|
276
|
-
await this._createDelegatedInvitations(dataSpace, [...space.spaceState.invitations.entries()]);
|
|
277
270
|
this.updated.emit();
|
|
278
271
|
}
|
|
279
272
|
},
|
|
@@ -296,43 +289,4 @@ export class DataSpaceManager {
|
|
|
296
289
|
this._spaces.set(metadata.key, dataSpace);
|
|
297
290
|
return dataSpace;
|
|
298
291
|
}
|
|
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
|
-
}
|
|
338
292
|
}
|
|
@@ -60,149 +60,146 @@ export const performInvitation = ({
|
|
|
60
60
|
const guestComplete = new Trigger<Result>();
|
|
61
61
|
const authCode = new Trigger<string>();
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
invariant(hostInvitation.swarmKey!.equals(guestInvitation.swarmKey!));
|
|
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)) {
|
|
80
77
|
break;
|
|
81
78
|
}
|
|
79
|
+
invariant(hostInvitation.swarmKey!.equals(guestInvitation.swarmKey!));
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
case Invitation.State.CONNECTED: {
|
|
84
|
+
hooks?.guest?.onConnected?.(guestObservable);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
await guestObservable.authenticate(await authCode.wait());
|
|
88
|
+
case Invitation.State.READY_FOR_AUTHENTICATION: {
|
|
89
|
+
if (hooks?.guest?.onReady?.(guestObservable)) {
|
|
93
90
|
break;
|
|
94
91
|
}
|
|
92
|
+
await guestObservable.authenticate(await authCode.wait());
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
case Invitation.State.AUTHENTICATING: {
|
|
97
|
+
hooks?.guest?.onAuthenticating?.(guestObservable);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
guestComplete.wake({ invitation: guestInvitation });
|
|
101
|
+
case Invitation.State.SUCCESS: {
|
|
102
|
+
if (hooks?.guest?.onSuccess?.(guestObservable)) {
|
|
106
103
|
break;
|
|
107
104
|
}
|
|
105
|
+
guestComplete.wake({ invitation: guestInvitation });
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
guestComplete.wake({ invitation: guestInvitation });
|
|
109
|
+
case Invitation.State.CANCELLED: {
|
|
110
|
+
if (hooks?.guest?.onCancelled?.(guestObservable)) {
|
|
114
111
|
break;
|
|
115
112
|
}
|
|
113
|
+
guestComplete.wake({ invitation: guestInvitation });
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
guestComplete.wake({ invitation: guestInvitation });
|
|
117
|
+
case Invitation.State.TIMEOUT: {
|
|
118
|
+
if (hooks?.guest?.onTimeout?.(guestObservable)) {
|
|
119
|
+
return;
|
|
122
120
|
}
|
|
121
|
+
guestComplete.wake({ invitation: guestInvitation });
|
|
123
122
|
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
(error: Error) => {
|
|
126
|
+
if (hooks?.guest?.onError?.(guestObservable)) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
guestComplete.wake({ error });
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
case Invitation.State.CONNECTED: {
|
|
136
|
+
hooks?.host?.onConnected?.(hostObservable);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
if (hostInvitation.authCode) {
|
|
145
|
-
authCode.wake(hostInvitation.authCode);
|
|
146
|
-
}
|
|
140
|
+
case Invitation.State.READY_FOR_AUTHENTICATION: {
|
|
141
|
+
if (hooks?.host?.onReady?.(hostObservable)) {
|
|
147
142
|
break;
|
|
148
143
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
hooks?.host?.onAuthenticating?.(hostObservable);
|
|
152
|
-
break;
|
|
144
|
+
if (hostInvitation.authCode) {
|
|
145
|
+
authCode.wake(hostInvitation.authCode);
|
|
153
146
|
}
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
case Invitation.State.AUTHENTICATING: {
|
|
151
|
+
hooks?.host?.onAuthenticating?.(hostObservable);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
154
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
hostComplete.wake({ invitation: hostInvitation });
|
|
155
|
+
case Invitation.State.SUCCESS: {
|
|
156
|
+
if (hooks?.host?.onSuccess?.(hostObservable)) {
|
|
160
157
|
break;
|
|
161
158
|
}
|
|
159
|
+
hostComplete.wake({ invitation: hostInvitation });
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
162
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
hostComplete.wake({ invitation: hostInvitation });
|
|
163
|
+
case Invitation.State.CANCELLED: {
|
|
164
|
+
if (hooks?.host?.onCancelled?.(hostObservable)) {
|
|
168
165
|
break;
|
|
169
166
|
}
|
|
167
|
+
hostComplete.wake({ invitation: hostInvitation });
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
hostComplete.wake({ invitation: hostInvitation });
|
|
171
|
+
case Invitation.State.TIMEOUT: {
|
|
172
|
+
if (hooks?.host?.onTimeout?.(hostObservable)) {
|
|
176
173
|
break;
|
|
177
174
|
}
|
|
175
|
+
hostComplete.wake({ invitation: hostInvitation });
|
|
176
|
+
break;
|
|
178
177
|
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
(error: Error) => {
|
|
181
|
+
if (hooks?.host?.onError?.(hostObservable)) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
hostComplete.wake({ error });
|
|
185
|
+
},
|
|
186
|
+
);
|
|
188
187
|
|
|
189
188
|
return [hostComplete.wait(), guestComplete.wait()];
|
|
190
189
|
};
|
|
191
190
|
|
|
192
|
-
const createInvitation =
|
|
191
|
+
const createInvitation = (
|
|
193
192
|
host: ServiceContext | InvitationHost,
|
|
194
193
|
options?: Partial<Invitation>,
|
|
195
|
-
):
|
|
194
|
+
): CancellableInvitation => {
|
|
196
195
|
options ??= {
|
|
197
196
|
authMethod: Invitation.AuthMethod.NONE,
|
|
198
197
|
...(options ?? {}),
|
|
199
198
|
};
|
|
200
199
|
|
|
201
200
|
if (host instanceof ServiceContext) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
...options,
|
|
205
|
-
});
|
|
201
|
+
const hostHandler = host.getInvitationHandler({ kind: Invitation.Kind.SPACE, ...options });
|
|
202
|
+
return host.invitations.createInvitation(hostHandler, options);
|
|
206
203
|
}
|
|
207
204
|
|
|
208
205
|
return host.share(options);
|