@dxos/client-services 0.5.3-main.f752aaa → 0.5.3-main.fffc127
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-3ZUGGKNX.mjs → chunk-OYIJIAJI.mjs} +293 -120
- package/dist/lib/browser/chunk-OYIJIAJI.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +3 -1
- package/dist/lib/browser/index.mjs.map +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +1 -1
- package/dist/lib/node/{chunk-TQQUIYBW.cjs → chunk-FIKOGQP2.cjs} +293 -119
- package/dist/lib/node/chunk-FIKOGQP2.cjs.map +7 -0
- package/dist/lib/node/index.cjs +45 -43
- package/dist/lib/node/index.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +8 -8
- package/dist/types/src/packlets/devtools/feeds.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/keys.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/metadata.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +2 -1
- 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 -0
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +1 -0
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +3 -2
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/services/util.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +2 -0
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +6 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +2 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/credential-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +2 -2
- 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 +36 -36
- package/src/packlets/diagnostics/diagnostics.ts +1 -0
- package/src/packlets/identity/identity-manager.ts +1 -0
- package/src/packlets/identity/identity.test.ts +3 -0
- package/src/packlets/invitations/device-invitation-protocol.ts +5 -1
- package/src/packlets/invitations/invitation-protocol.ts +2 -0
- package/src/packlets/invitations/invitations-manager.ts +5 -0
- package/src/packlets/invitations/space-invitation-protocol.ts +29 -2
- package/src/packlets/spaces/data-space-manager.ts +100 -10
- package/src/packlets/spaces/data-space.ts +35 -8
- package/src/packlets/spaces/spaces-service.ts +46 -15
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-3ZUGGKNX.mjs.map +0 -7
- package/dist/lib/node/chunk-TQQUIYBW.cjs.map +0 -7
|
@@ -3,26 +3,43 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Event, synchronized, trackLeaks } from '@dxos/async';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { type Doc } from '@dxos/automerge/automerge';
|
|
7
|
+
import { type AutomergeUrl } from '@dxos/automerge/automerge-repo';
|
|
8
|
+
import { cancelWithContext, Context } from '@dxos/context';
|
|
9
|
+
import {
|
|
10
|
+
type CredentialSigner,
|
|
11
|
+
type DelegateInvitationCredential,
|
|
12
|
+
getCredentialAssertion,
|
|
13
|
+
type MemberInfo,
|
|
14
|
+
} from '@dxos/credentials';
|
|
8
15
|
import { type EchoHost } from '@dxos/echo-db';
|
|
9
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
AuthStatus,
|
|
18
|
+
type MetadataStore,
|
|
19
|
+
type Space,
|
|
20
|
+
type SpaceManager,
|
|
21
|
+
type SpaceProtocol,
|
|
22
|
+
type SpaceProtocolSession,
|
|
23
|
+
} from '@dxos/echo-pipeline';
|
|
24
|
+
import { type SpaceDoc } from '@dxos/echo-protocol';
|
|
10
25
|
import { type FeedStore } from '@dxos/feed-store';
|
|
11
26
|
import { invariant } from '@dxos/invariant';
|
|
12
27
|
import { type Keyring } from '@dxos/keyring';
|
|
13
28
|
import { PublicKey } from '@dxos/keys';
|
|
14
29
|
import { log } from '@dxos/log';
|
|
15
|
-
import { trace } from '@dxos/protocols';
|
|
30
|
+
import { trace as Trace } from '@dxos/protocols';
|
|
16
31
|
import { Invitation, SpaceState } from '@dxos/protocols/proto/dxos/client/services';
|
|
17
32
|
import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
18
33
|
import { type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
19
|
-
import { type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
34
|
+
import { type Credential, type ProfileDocument, SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
20
35
|
import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
36
|
+
import { type PeerState } from '@dxos/protocols/proto/dxos/mesh/presence';
|
|
21
37
|
import { Gossip, Presence } from '@dxos/teleport-extension-gossip';
|
|
22
38
|
import { type Timeframe } from '@dxos/timeframe';
|
|
39
|
+
import { trace } from '@dxos/tracing';
|
|
23
40
|
import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
|
|
24
41
|
|
|
25
|
-
import { DataSpace } from './data-space';
|
|
42
|
+
import { DataSpace, findPropertiesObject } from './data-space';
|
|
26
43
|
import { spaceGenesis } from './genesis';
|
|
27
44
|
import { createAuthProvider } from '../identity';
|
|
28
45
|
import { type InvitationsManager } from '../invitations';
|
|
@@ -90,6 +107,31 @@ export class DataSpaceManager {
|
|
|
90
107
|
} = params ?? {};
|
|
91
108
|
this._spaceMemberPresenceAnnounceInterval = spaceMemberPresenceAnnounceInterval;
|
|
92
109
|
this._spaceMemberPresenceOfflineTimeout = spaceMemberPresenceOfflineTimeout;
|
|
110
|
+
|
|
111
|
+
trace.diagnostic({
|
|
112
|
+
id: 'spaces',
|
|
113
|
+
name: 'Spaces',
|
|
114
|
+
fetch: async () => {
|
|
115
|
+
return Array.from(this._spaces.values()).map((space) => {
|
|
116
|
+
const rootUrl = space.automergeSpaceState.rootUrl;
|
|
117
|
+
const rootHandle = rootUrl ? this._echoHost.automergeRepo.find(rootUrl as AutomergeUrl) : undefined;
|
|
118
|
+
const rootDoc = rootHandle?.docSync() as Doc<SpaceDoc> | undefined;
|
|
119
|
+
|
|
120
|
+
const properties = rootDoc && findPropertiesObject(rootDoc);
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
key: space.key.toHex(),
|
|
124
|
+
state: SpaceState[space.state],
|
|
125
|
+
name: properties?.[1].data.name ?? null,
|
|
126
|
+
inlineObjects: rootDoc ? Object.keys(rootDoc.objects ?? {}).length : null,
|
|
127
|
+
linkedObjects: rootDoc ? Object.keys(rootDoc.links ?? {}).length : null,
|
|
128
|
+
credentials: space.inner.spaceState.credentials.length,
|
|
129
|
+
members: space.inner.spaceState.members.size,
|
|
130
|
+
rootUrl,
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
});
|
|
93
135
|
}
|
|
94
136
|
|
|
95
137
|
// TODO(burdon): Remove.
|
|
@@ -100,7 +142,7 @@ export class DataSpaceManager {
|
|
|
100
142
|
@synchronized
|
|
101
143
|
async open() {
|
|
102
144
|
log('open');
|
|
103
|
-
log.trace('dxos.echo.data-space-manager.open',
|
|
145
|
+
log.trace('dxos.echo.data-space-manager.open', Trace.begin({ id: this._instanceId }));
|
|
104
146
|
log('metadata loaded', { spaces: this._metadataStore.spaces.length });
|
|
105
147
|
|
|
106
148
|
await forEachAsync(this._metadataStore.spaces, async (spaceMetadata) => {
|
|
@@ -121,7 +163,7 @@ export class DataSpaceManager {
|
|
|
121
163
|
}
|
|
122
164
|
}
|
|
123
165
|
|
|
124
|
-
log.trace('dxos.echo.data-space-manager.open',
|
|
166
|
+
log.trace('dxos.echo.data-space-manager.open', Trace.end({ id: this._instanceId }));
|
|
125
167
|
}
|
|
126
168
|
|
|
127
169
|
@synchronized
|
|
@@ -153,10 +195,10 @@ export class DataSpaceManager {
|
|
|
153
195
|
|
|
154
196
|
log('creating space...', { spaceKey });
|
|
155
197
|
|
|
156
|
-
const
|
|
198
|
+
const root = await this._echoHost.createSpaceRoot(spaceKey);
|
|
157
199
|
const space = await this._constructSpace(metadata);
|
|
158
200
|
|
|
159
|
-
const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner,
|
|
201
|
+
const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner, root.url);
|
|
160
202
|
await this._metadataStore.addSpace(metadata);
|
|
161
203
|
|
|
162
204
|
const memberCredential = credentials[1];
|
|
@@ -246,6 +288,11 @@ export class DataSpaceManager {
|
|
|
246
288
|
onAuthFailure: () => {
|
|
247
289
|
log.warn('auth failure');
|
|
248
290
|
},
|
|
291
|
+
onMemberRolesChanged: async (members: MemberInfo[]) => {
|
|
292
|
+
if (dataSpace?.state === SpaceState.READY) {
|
|
293
|
+
this._handleMemberRoleChanges(presence, space.protocol, members);
|
|
294
|
+
}
|
|
295
|
+
},
|
|
249
296
|
memberKey: this._signingContext.identityKey,
|
|
250
297
|
onDelegatedInvitationStatusChange: (invitation, isActive) => {
|
|
251
298
|
return this._handleInvitationStatusChange(dataSpace, invitation, isActive);
|
|
@@ -272,6 +319,7 @@ export class DataSpaceManager {
|
|
|
272
319
|
log('after space ready', { space: space.key, open: this._isOpen });
|
|
273
320
|
if (this._isOpen) {
|
|
274
321
|
await this._createDelegatedInvitations(dataSpace, [...space.spaceState.invitations.entries()]);
|
|
322
|
+
this._handleMemberRoleChanges(presence, space.protocol, [...space.spaceState.members.values()]);
|
|
275
323
|
this.updated.emit();
|
|
276
324
|
}
|
|
277
325
|
},
|
|
@@ -282,6 +330,12 @@ export class DataSpaceManager {
|
|
|
282
330
|
cache: metadata.cache,
|
|
283
331
|
});
|
|
284
332
|
|
|
333
|
+
presence.newPeer.on((peerState) => {
|
|
334
|
+
if (dataSpace.state === SpaceState.READY) {
|
|
335
|
+
this._handleNewPeerConnected(space, peerState);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
285
339
|
if (metadata.state !== SpaceState.INACTIVE) {
|
|
286
340
|
await dataSpace.open();
|
|
287
341
|
}
|
|
@@ -294,6 +348,42 @@ export class DataSpaceManager {
|
|
|
294
348
|
return dataSpace;
|
|
295
349
|
}
|
|
296
350
|
|
|
351
|
+
private _handleMemberRoleChanges(presence: Presence, spaceProtocol: SpaceProtocol, memberInfo: MemberInfo[]): void {
|
|
352
|
+
let closedSessions = 0;
|
|
353
|
+
for (const member of memberInfo) {
|
|
354
|
+
if (member.key.equals(presence.getLocalState().identityKey)) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const peers = presence.getPeersByIdentityKey(member.key);
|
|
358
|
+
const sessions = peers.map((p) => p.peerId && spaceProtocol.sessions.get(p.peerId));
|
|
359
|
+
const sessionsToClose = sessions.filter((s): s is SpaceProtocolSession => {
|
|
360
|
+
return (s && (member.role === SpaceMember.Role.REMOVED) !== (s.authStatus === AuthStatus.FAILURE)) ?? false;
|
|
361
|
+
});
|
|
362
|
+
sessionsToClose.forEach((session) => {
|
|
363
|
+
void session.close().catch(log.error);
|
|
364
|
+
});
|
|
365
|
+
closedSessions += sessionsToClose.length;
|
|
366
|
+
}
|
|
367
|
+
log('processed member role changes', {
|
|
368
|
+
roleChangeCount: memberInfo.length,
|
|
369
|
+
peersOnline: presence.getPeersOnline().length,
|
|
370
|
+
closedSessions,
|
|
371
|
+
});
|
|
372
|
+
// Handle the case when there was a removed peer online, we can now establish a connection with them
|
|
373
|
+
spaceProtocol.updateTopology();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private _handleNewPeerConnected(space: Space, peerState: PeerState): void {
|
|
377
|
+
const role = space.spaceState.getMemberRole(peerState.identityKey);
|
|
378
|
+
if (role === SpaceMember.Role.REMOVED) {
|
|
379
|
+
const session = peerState.peerId && space.protocol.sessions.get(peerState.peerId);
|
|
380
|
+
if (session != null) {
|
|
381
|
+
log('closing a session with a removed peer', { peerId: peerState.peerId });
|
|
382
|
+
void session.close().catch(log.error);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
297
387
|
private async _handleInvitationStatusChange(
|
|
298
388
|
dataSpace: DataSpace | undefined,
|
|
299
389
|
delegatedInvitation: DelegateInvitationCredential,
|
|
@@ -4,12 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
import { Event, asyncTimeout, scheduleTask, sleep, synchronized, trackLeaks } from '@dxos/async';
|
|
6
6
|
import { AUTH_TIMEOUT } from '@dxos/client-protocol';
|
|
7
|
-
import {
|
|
7
|
+
import { Context, ContextDisposedError, cancelWithContext } from '@dxos/context';
|
|
8
8
|
import { timed, warnAfterTimeout } from '@dxos/debug';
|
|
9
9
|
import { type EchoHost } from '@dxos/echo-db';
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
import {
|
|
11
|
+
AutomergeDocumentLoaderImpl,
|
|
12
|
+
createMappedFeedWriter,
|
|
13
|
+
type MetadataStore,
|
|
14
|
+
type Space,
|
|
15
|
+
} from '@dxos/echo-pipeline';
|
|
16
|
+
import { type ObjectStructure, type SpaceDoc } from '@dxos/echo-protocol';
|
|
13
17
|
import { TYPE_PROPERTIES } from '@dxos/echo-schema';
|
|
14
18
|
import { type FeedStore } from '@dxos/feed-store';
|
|
15
19
|
import { failedInvariant, invariant } from '@dxos/invariant';
|
|
@@ -17,15 +21,15 @@ import { type Keyring } from '@dxos/keyring';
|
|
|
17
21
|
import { PublicKey } from '@dxos/keys';
|
|
18
22
|
import { log } from '@dxos/log';
|
|
19
23
|
import { CancelledError, SystemError } from '@dxos/protocols';
|
|
20
|
-
import { SpaceState, type Space as SpaceProto
|
|
24
|
+
import { CreateEpochRequest, SpaceState, type Space as SpaceProto } from '@dxos/protocols/proto/dxos/client/services';
|
|
21
25
|
import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
22
26
|
import { type SpaceCache } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
23
|
-
import { SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
24
27
|
import {
|
|
25
28
|
AdmittedFeed,
|
|
26
|
-
|
|
29
|
+
SpaceMember,
|
|
27
30
|
type Credential,
|
|
28
31
|
type Epoch,
|
|
32
|
+
type ProfileDocument,
|
|
29
33
|
} from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
30
34
|
import { type GossipMessage } from '@dxos/protocols/proto/dxos/mesh/teleport/gossip';
|
|
31
35
|
import { type Gossip, type Presence } from '@dxos/teleport-extension-gossip';
|
|
@@ -360,6 +364,8 @@ export class DataSpace {
|
|
|
360
364
|
|
|
361
365
|
private _onNewAutomergeRoot(rootUrl: string) {
|
|
362
366
|
log('loading automerge root doc for space', { space: this.key, rootUrl });
|
|
367
|
+
// Override share policy = true for the root document.
|
|
368
|
+
// Workaround for https://github.com/automerge/automerge-repo/pull/292
|
|
363
369
|
this._echoHost.replicateDocument(rootUrl);
|
|
364
370
|
const handle = this._echoHost.automergeRepo.find(rootUrl as any);
|
|
365
371
|
|
|
@@ -378,6 +384,14 @@ export class DataSpace {
|
|
|
378
384
|
doc.access = { spaceKey: this.key.toHex() };
|
|
379
385
|
});
|
|
380
386
|
}
|
|
387
|
+
|
|
388
|
+
// TODO(dmaretskyi): Close roots.
|
|
389
|
+
// TODO(dmaretskyi): How do we handle changing to the next EPOCH?
|
|
390
|
+
if (!this._echoHost.roots.has(handle.documentId)) {
|
|
391
|
+
await this._echoHost.openSpaceRoot(handle.url);
|
|
392
|
+
} else {
|
|
393
|
+
log.warn('echo database root already exists', { space: this.key, rootUrl });
|
|
394
|
+
}
|
|
381
395
|
} catch (err) {
|
|
382
396
|
if (err instanceof ContextDisposedError) {
|
|
383
397
|
return;
|
|
@@ -452,7 +466,7 @@ export class DataSpace {
|
|
|
452
466
|
|
|
453
467
|
// Find properties object.
|
|
454
468
|
const objects = Object.entries((rootHandle.docSync() as SpaceDoc).objects!);
|
|
455
|
-
const properties =
|
|
469
|
+
const properties = findPropertiesObject(rootHandle.docSync() as SpaceDoc);
|
|
456
470
|
const otherObjects = objects.filter(([key]) => key !== properties?.[0]);
|
|
457
471
|
invariant(properties, 'Properties not found');
|
|
458
472
|
|
|
@@ -529,3 +543,16 @@ export class DataSpace {
|
|
|
529
543
|
this.stateUpdate.emit();
|
|
530
544
|
}
|
|
531
545
|
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Assumes properties are at root.
|
|
549
|
+
*/
|
|
550
|
+
export const findPropertiesObject = (spaceDoc: SpaceDoc): [string, ObjectStructure] | undefined => {
|
|
551
|
+
for (const id in spaceDoc.objects ?? {}) {
|
|
552
|
+
const obj = spaceDoc.objects![id];
|
|
553
|
+
if (obj.system.type?.itemId === TYPE_PROPERTIES) {
|
|
554
|
+
return [id, obj];
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return undefined;
|
|
558
|
+
};
|
|
@@ -4,12 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
import { EventSubscriptions, UpdateScheduler, scheduleTask } from '@dxos/async';
|
|
6
6
|
import { Stream } from '@dxos/codec-protobuf';
|
|
7
|
-
import { type CredentialProcessor } from '@dxos/credentials';
|
|
7
|
+
import { createAdmissionCredentials, type CredentialProcessor, getCredentialAssertion } from '@dxos/credentials';
|
|
8
8
|
import { raise } from '@dxos/debug';
|
|
9
9
|
import { type SpaceManager } from '@dxos/echo-pipeline';
|
|
10
|
+
import { writeMessages } from '@dxos/feed-store';
|
|
10
11
|
import { invariant } from '@dxos/invariant';
|
|
11
12
|
import { log } from '@dxos/log';
|
|
12
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
ApiError,
|
|
15
|
+
SpaceNotFoundError,
|
|
16
|
+
encodeError,
|
|
17
|
+
IdentityNotInitializedError,
|
|
18
|
+
AuthorizationError,
|
|
19
|
+
} from '@dxos/protocols';
|
|
13
20
|
import {
|
|
14
21
|
SpaceMember,
|
|
15
22
|
SpaceState,
|
|
@@ -24,7 +31,7 @@ import {
|
|
|
24
31
|
type WriteCredentialsRequest,
|
|
25
32
|
type UpdateMemberRoleRequest,
|
|
26
33
|
} from '@dxos/protocols/proto/dxos/client/services';
|
|
27
|
-
import { type Credential
|
|
34
|
+
import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
28
35
|
import { type GossipMessage } from '@dxos/protocols/proto/dxos/mesh/teleport/gossip';
|
|
29
36
|
import { type Provider } from '@dxos/util';
|
|
30
37
|
|
|
@@ -40,10 +47,7 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
40
47
|
) {}
|
|
41
48
|
|
|
42
49
|
async createSpace(): Promise<Space> {
|
|
43
|
-
|
|
44
|
-
throw new Error('This device has no HALO identity available. See https://docs.dxos.org/guide/platform/halo');
|
|
45
|
-
}
|
|
46
|
-
|
|
50
|
+
this._requireIdentity();
|
|
47
51
|
const dataSpaceManager = await this._getDataSpaceManager();
|
|
48
52
|
const space = await dataSpaceManager.createSpace();
|
|
49
53
|
return this._serializeSpace(space);
|
|
@@ -68,8 +72,30 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
|
|
71
|
-
async updateMemberRole(
|
|
72
|
-
|
|
75
|
+
async updateMemberRole(request: UpdateMemberRoleRequest): Promise<void> {
|
|
76
|
+
const identity = this._requireIdentity();
|
|
77
|
+
const space = this._spaceManager.spaces.get(request.spaceKey);
|
|
78
|
+
if (space == null) {
|
|
79
|
+
throw new SpaceNotFoundError(request.spaceKey);
|
|
80
|
+
}
|
|
81
|
+
if (!space.spaceState.hasMembershipManagementPermission(identity.identityKey)) {
|
|
82
|
+
throw new AuthorizationError('No member management permission.', {
|
|
83
|
+
spaceKey: space.key,
|
|
84
|
+
role: space.spaceState.getMemberRole(identity.identityKey),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const credentials = await createAdmissionCredentials(
|
|
88
|
+
identity.getIdentityCredentialSigner(),
|
|
89
|
+
request.memberKey,
|
|
90
|
+
space.key,
|
|
91
|
+
space.genesisFeedKey,
|
|
92
|
+
request.newRole,
|
|
93
|
+
space.spaceState.membershipChainHeads,
|
|
94
|
+
);
|
|
95
|
+
invariant(credentials[0].credential);
|
|
96
|
+
const spaceMemberCredential = credentials[0].credential.credential;
|
|
97
|
+
invariant(getCredentialAssertion(spaceMemberCredential)['@type'] === 'dxos.halo.credentials.SpaceMember');
|
|
98
|
+
await writeMessages(space.controlPipeline.writer, credentials);
|
|
73
99
|
}
|
|
74
100
|
|
|
75
101
|
querySpaces(): Stream<QuerySpacesResponse> {
|
|
@@ -218,12 +244,8 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
218
244
|
identityKey: member.key,
|
|
219
245
|
profile: member.profile ?? {},
|
|
220
246
|
},
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
? SpaceMember.PresenceState.REMOVED
|
|
224
|
-
: isMe || peers.length > 0
|
|
225
|
-
? SpaceMember.PresenceState.ONLINE
|
|
226
|
-
: SpaceMember.PresenceState.OFFLINE,
|
|
247
|
+
role: member.role,
|
|
248
|
+
presence: peers.length > 0 ? SpaceMember.PresenceState.ONLINE : SpaceMember.PresenceState.OFFLINE,
|
|
227
249
|
peerStates: peers,
|
|
228
250
|
};
|
|
229
251
|
}),
|
|
@@ -232,6 +254,15 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
232
254
|
metrics: space.metrics,
|
|
233
255
|
};
|
|
234
256
|
}
|
|
257
|
+
|
|
258
|
+
private _requireIdentity() {
|
|
259
|
+
if (!this._identityManager.identity) {
|
|
260
|
+
throw new IdentityNotInitializedError(
|
|
261
|
+
'This device has no HALO identity available. See https://docs.dxos.org/guide/platform/halo',
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
return this._identityManager.identity;
|
|
265
|
+
}
|
|
235
266
|
}
|
|
236
267
|
|
|
237
268
|
// Add `user-channel` prefix to the channel name, so that it doesn't collide with the internal channels.
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DXOS_VERSION = "0.5.3-main.
|
|
1
|
+
export const DXOS_VERSION = "0.5.3-main.fffc127";
|