@dxos/client-services 0.7.5-main.9d2a38b → 0.7.5-main.e94eead

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 (55) hide show
  1. package/dist/lib/browser/{chunk-RFZHCLFI.mjs → chunk-5QA4IAAU.mjs} +262 -181
  2. package/dist/lib/browser/chunk-5QA4IAAU.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/testing/index.mjs +1 -1
  6. package/dist/lib/node/{chunk-WCTGAZLA.cjs → chunk-FQ5WZIGO.cjs} +255 -174
  7. package/dist/lib/node/chunk-FQ5WZIGO.cjs.map +7 -0
  8. package/dist/lib/node/index.cjs +47 -47
  9. package/dist/lib/node/meta.json +1 -1
  10. package/dist/lib/node/testing/index.cjs +8 -8
  11. package/dist/lib/node-esm/{chunk-RSOLUJE6.mjs → chunk-YAOQ7FPR.mjs} +262 -181
  12. package/dist/lib/node-esm/chunk-YAOQ7FPR.mjs.map +7 -0
  13. package/dist/lib/node-esm/index.mjs +1 -1
  14. package/dist/lib/node-esm/meta.json +1 -1
  15. package/dist/lib/node-esm/testing/index.mjs +1 -1
  16. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -1
  17. package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts.map +1 -1
  18. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
  19. package/dist/types/src/packlets/identity/identity-manager.d.ts +0 -3
  20. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  21. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +12 -4
  22. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
  23. package/dist/types/src/packlets/identity/identity-service.d.ts +8 -3
  24. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  25. package/dist/types/src/packlets/identity/identity.d.ts +3 -1
  26. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  27. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  28. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  29. package/dist/types/src/packlets/invitations/utils.d.ts +1 -0
  30. package/dist/types/src/packlets/invitations/utils.d.ts.map +1 -1
  31. package/dist/types/src/packlets/services/service-host.d.ts +1 -1
  32. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  33. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  34. package/dist/types/src/version.d.ts +1 -1
  35. package/package.json +39 -38
  36. package/src/packlets/agents/edge-agent-manager.ts +6 -0
  37. package/src/packlets/diagnostics/diagnostics-collector.ts +2 -2
  38. package/src/packlets/diagnostics/diagnostics.ts +17 -13
  39. package/src/packlets/identity/identity-manager.ts +3 -29
  40. package/src/packlets/identity/identity-recovery-manager.ts +95 -10
  41. package/src/packlets/identity/identity-service.ts +16 -3
  42. package/src/packlets/identity/identity.test.ts +2 -1
  43. package/src/packlets/identity/identity.ts +4 -1
  44. package/src/packlets/invitations/invitations-handler.ts +15 -6
  45. package/src/packlets/invitations/invitations-manager.ts +1 -1
  46. package/src/packlets/invitations/space-invitation-protocol.ts +2 -3
  47. package/src/packlets/invitations/utils.ts +7 -0
  48. package/src/packlets/spaces/data-space-manager.ts +3 -3
  49. package/src/packlets/spaces/data-space.ts +2 -2
  50. package/src/packlets/spaces/edge-feed-replicator.ts +8 -8
  51. package/src/packlets/spaces/spaces-service.ts +30 -20
  52. package/src/version.ts +1 -1
  53. package/dist/lib/browser/chunk-RFZHCLFI.mjs.map +0 -7
  54. package/dist/lib/node/chunk-WCTGAZLA.cjs.map +0 -7
  55. package/dist/lib/node-esm/chunk-RSOLUJE6.mjs.map +0 -7
@@ -9,8 +9,16 @@ import { invariant } from '@dxos/invariant';
9
9
  import { type Keyring } from '@dxos/keyring';
10
10
  import { PublicKey } from '@dxos/keys';
11
11
  import { log } from '@dxos/log';
12
- import { EdgeAuthChallengeError, type RecoverIdentityRequest, type RecoverIdentityResponseBody } from '@dxos/protocols';
12
+ import {
13
+ EdgeAuthChallengeError,
14
+ type RecoverIdentityRequest as EdgeRecoverIdentityRequest,
15
+ type RecoverIdentityResponseBody,
16
+ } from '@dxos/protocols';
13
17
  import { schema } from '@dxos/protocols/proto';
18
+ import {
19
+ type CreateRecoveryCredentialRequest,
20
+ type RecoverIdentityRequest,
21
+ } from '@dxos/protocols/proto/dxos/client/services';
14
22
  import { Timeframe } from '@dxos/timeframe';
15
23
 
16
24
  import { type Identity } from './identity';
@@ -24,13 +32,26 @@ export class EdgeIdentityRecoveryManager {
24
32
  private readonly _acceptRecoveredIdentity: (params: JoinIdentityParams) => Promise<Identity>,
25
33
  ) {}
26
34
 
27
- public async createRecoveryPhrase() {
35
+ public async createRecoveryCredential({ data }: CreateRecoveryCredentialRequest) {
28
36
  const identity = this._identityProvider();
29
37
  invariant(identity);
30
38
 
31
- const seedphrase = generateSeedPhrase();
32
- const keypair = keyPairFromSeedPhrase(seedphrase);
33
- const recoveryKey = PublicKey.from(keypair.publicKey);
39
+ let recoveryKey: PublicKey;
40
+ let lookupKey: PublicKey;
41
+ let algorithm: string;
42
+ let recoveryCode: string | undefined;
43
+ if (data) {
44
+ recoveryKey = data.recoveryKey;
45
+ lookupKey = data.lookupKey;
46
+ algorithm = data.algorithm;
47
+ } else {
48
+ recoveryCode = generateSeedPhrase();
49
+ const keypair = keyPairFromSeedPhrase(recoveryCode);
50
+ recoveryKey = PublicKey.from(keypair.publicKey);
51
+ lookupKey = PublicKey.from(keypair.publicKey);
52
+ algorithm = 'ED25519';
53
+ }
54
+
34
55
  const identityKey = identity.identityKey;
35
56
  const credential = await identity.getIdentityCredentialSigner().createCredential({
36
57
  subject: identityKey,
@@ -38,24 +59,88 @@ export class EdgeIdentityRecoveryManager {
38
59
  '@type': 'dxos.halo.credentials.IdentityRecovery',
39
60
  recoveryKey,
40
61
  identityKey,
62
+ algorithm,
63
+ lookupKey,
41
64
  },
42
65
  });
43
66
 
44
67
  const receipt = await identity.controlPipeline.writer.write({ credential: { credential } });
45
68
  await identity.controlPipeline.state.waitUntilTimeframe(new Timeframe([[receipt.feedKey, receipt.seq]]));
46
69
 
47
- return { seedphrase };
70
+ return { recoveryCode };
71
+ }
72
+
73
+ public async requestRecoveryChallenge() {
74
+ invariant(this._edgeClient, 'Not connected to EDGE.');
75
+
76
+ const deviceKey = await this._keyring.createKey();
77
+ const controlFeedKey = await this._keyring.createKey();
78
+ const request: EdgeRecoverIdentityRequest = {
79
+ deviceKey: deviceKey.toHex(),
80
+ controlFeedKey: controlFeedKey.toHex(),
81
+ };
82
+
83
+ try {
84
+ await this._edgeClient.recoverIdentity(request);
85
+ throw new Error('No challenge received.');
86
+ } catch (error: any) {
87
+ if (!(error instanceof EdgeAuthChallengeError)) {
88
+ throw error;
89
+ }
90
+ return {
91
+ deviceKey,
92
+ controlFeedKey,
93
+ challenge: error.challenge,
94
+ };
95
+ }
96
+ }
97
+
98
+ public async recoverIdentityWithExternalSignature({
99
+ lookupKey,
100
+ deviceKey,
101
+ controlFeedKey,
102
+ signature,
103
+ clientDataJson,
104
+ authenticatorData,
105
+ }: RecoverIdentityRequest.ExternalSignature) {
106
+ invariant(this._edgeClient, 'Not connected to EDGE.');
107
+
108
+ const request: EdgeRecoverIdentityRequest = {
109
+ lookupKey: lookupKey.toHex(),
110
+ deviceKey: deviceKey.toHex(),
111
+ controlFeedKey: controlFeedKey.toHex(),
112
+ signature:
113
+ clientDataJson && authenticatorData
114
+ ? {
115
+ signature: Buffer.from(signature).toString('base64'),
116
+ clientDataJson: Buffer.from(clientDataJson).toString('base64'),
117
+ authenticatorData: Buffer.from(authenticatorData).toString('base64'),
118
+ }
119
+ : Buffer.from(signature).toString('base64'),
120
+ };
121
+
122
+ const response = await this._edgeClient.recoverIdentity(request);
123
+
124
+ await this._acceptRecoveredIdentity({
125
+ authorizedDeviceCredential: decodeCredential(response.deviceAuthCredential),
126
+ haloGenesisFeedKey: PublicKey.fromHex(response.genesisFeedKey),
127
+ haloSpaceKey: PublicKey.fromHex(response.haloSpaceKey),
128
+ identityKey: PublicKey.fromHex(response.identityKey),
129
+ deviceKey,
130
+ controlFeedKey,
131
+ dataFeedKey: await this._keyring.createKey(),
132
+ });
48
133
  }
49
134
 
50
- public async recoverIdentity(args: { seedphrase: string }) {
135
+ public async recoverIdentity({ recoveryCode }: { recoveryCode: string }) {
51
136
  invariant(this._edgeClient, 'Not connected to EDGE.');
52
137
 
53
- const recoveryKeypair = keyPairFromSeedPhrase(args.seedphrase);
138
+ const recoveryKeypair = keyPairFromSeedPhrase(recoveryCode);
54
139
  const recoveryKey = PublicKey.from(recoveryKeypair.publicKey);
55
140
  const deviceKey = await this._keyring.createKey();
56
141
  const controlFeedKey = await this._keyring.createKey();
57
- const request: RecoverIdentityRequest = {
58
- recoveryKey: recoveryKey.toHex(),
142
+ const request: EdgeRecoverIdentityRequest = {
143
+ lookupKey: recoveryKey.toHex(),
59
144
  deviceKey: deviceKey.toHex(),
60
145
  controlFeedKey: controlFeedKey.toHex(),
61
146
  };
@@ -11,6 +11,7 @@ import { type Keyring } from '@dxos/keyring';
11
11
  import { log } from '@dxos/log';
12
12
  import {
13
13
  type CreateIdentityRequest,
14
+ type CreateRecoveryCredentialRequest,
14
15
  type Identity as IdentityProto,
15
16
  type IdentityService,
16
17
  type QueryIdentityResponse,
@@ -76,6 +77,7 @@ export class IdentityServiceImpl extends Resource implements IdentityService {
76
77
  }
77
78
 
78
79
  return {
80
+ did: this._identityManager.identity.did,
79
81
  identityKey: this._identityManager.identity.identityKey,
80
82
  spaceKey: this._identityManager.identity.space.key,
81
83
  profile: this._identityManager.identity.profileDocument,
@@ -89,12 +91,23 @@ export class IdentityServiceImpl extends Resource implements IdentityService {
89
91
  return this._getIdentity()!;
90
92
  }
91
93
 
92
- async createRecoveryPhrase() {
93
- return this._recoveryManager.createRecoveryPhrase();
94
+ async createRecoveryCredential(request: CreateRecoveryCredentialRequest) {
95
+ return this._recoveryManager.createRecoveryCredential(request);
96
+ }
97
+
98
+ async requestRecoveryChallenge() {
99
+ return this._recoveryManager.requestRecoveryChallenge();
94
100
  }
95
101
 
96
102
  async recoverIdentity(request: RecoverIdentityRequest): Promise<IdentityProto> {
97
- await this._recoveryManager.recoverIdentity(request);
103
+ if (request.recoveryCode) {
104
+ await this._recoveryManager.recoverIdentity({ recoveryCode: request.recoveryCode });
105
+ } else if (request.external) {
106
+ await this._recoveryManager.recoverIdentityWithExternalSignature(request.external);
107
+ } else {
108
+ throw new Error('Invalid request.');
109
+ }
110
+
98
111
  return this._getIdentity()!;
99
112
  }
100
113
 
@@ -5,7 +5,7 @@
5
5
  import { onTestFinished, describe, expect, test } from 'vitest';
6
6
 
7
7
  import { Context } from '@dxos/context';
8
- import { CredentialGenerator, verifyCredential } from '@dxos/credentials';
8
+ import { createDidFromIdentityKey, CredentialGenerator, verifyCredential } from '@dxos/credentials';
9
9
  import {
10
10
  createIdFromSpaceKey,
11
11
  MetadataStore,
@@ -206,6 +206,7 @@ describe('identity/identity', () => {
206
206
 
207
207
  const identity = new Identity({
208
208
  signer: keyring,
209
+ did: await createDidFromIdentityKey(identityKey),
209
210
  identityKey,
210
211
  deviceKey,
211
212
  space,
@@ -17,7 +17,7 @@ import { type Space } from '@dxos/echo-pipeline';
17
17
  import { type EdgeConnection } from '@dxos/edge-client';
18
18
  import { writeMessages, type FeedWrapper } from '@dxos/feed-store';
19
19
  import { invariant } from '@dxos/invariant';
20
- import { PublicKey, type SpaceId } from '@dxos/keys';
20
+ import { type IdentityDid, PublicKey, type SpaceId } from '@dxos/keys';
21
21
  import { log } from '@dxos/log';
22
22
  import { type Runtime } from '@dxos/protocols/proto/dxos/config';
23
23
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
@@ -38,6 +38,7 @@ import { DefaultSpaceStateMachine } from './default-space-state-machine';
38
38
  import { EdgeFeedReplicator } from '../spaces';
39
39
 
40
40
  export type IdentityParams = {
41
+ did: IdentityDid;
41
42
  identityKey: PublicKey;
42
43
  deviceKey: PublicKey;
43
44
  signer: Signer;
@@ -63,6 +64,7 @@ export class Identity {
63
64
 
64
65
  public readonly authVerifier: TrustedKeySetAuthVerifier;
65
66
 
67
+ public readonly did: IdentityDid;
66
68
  public readonly identityKey: PublicKey;
67
69
  public readonly deviceKey: PublicKey;
68
70
 
@@ -73,6 +75,7 @@ export class Identity {
73
75
  this._signer = params.signer;
74
76
  this._presence = params.presence;
75
77
 
78
+ this.did = params.did;
76
79
  this.identityKey = params.identityKey;
77
80
  this.deviceKey = params.deviceKey;
78
81
 
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { type PushStream, scheduleTask, TimeoutError, type Trigger } from '@dxos/async';
6
- import { INVITATION_TIMEOUT } from '@dxos/client-protocol';
6
+ import { INVITATION_TIMEOUT, getExpirationTime } from '@dxos/client-protocol';
7
7
  import { type Context, ContextDisposedError } from '@dxos/context';
8
8
  import { createKeyPair, sign } from '@dxos/crypto';
9
9
  import { type EdgeHttpClient } from '@dxos/edge-client';
@@ -11,7 +11,7 @@ import { invariant } from '@dxos/invariant';
11
11
  import { PublicKey } from '@dxos/keys';
12
12
  import { log } from '@dxos/log';
13
13
  import { createTeleportProtocolFactory, type SwarmNetworkManager, type SwarmConnection } from '@dxos/network-manager';
14
- import { InvalidInvitationExtensionRoleError, trace } from '@dxos/protocols';
14
+ import { InvalidInvitationError, InvalidInvitationExtensionRoleError, trace } from '@dxos/protocols';
15
15
  import { type AdmissionKeypair, Invitation } from '@dxos/protocols/proto/dxos/client/services';
16
16
  import { type DeviceProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
17
17
  import { AuthenticationResponse, type IntroductionResponse } from '@dxos/protocols/proto/dxos/halo/invitations';
@@ -188,8 +188,9 @@ export class InvitationsHandler {
188
188
  return extension;
189
189
  };
190
190
 
191
- if (invitation.lifetime && invitation.created) {
192
- if (invitation.created.getTime() + invitation.lifetime * 1000 < Date.now()) {
191
+ const expiresOn = getExpirationTime(invitation);
192
+ if (expiresOn) {
193
+ if (expiresOn.getTime() < Date.now()) {
193
194
  log.warn('invitation has already expired');
194
195
  guardedState.set(null, Invitation.State.EXPIRED);
195
196
  void ctx.dispose().catch((err) => log.catch(err));
@@ -204,7 +205,7 @@ export class InvitationsHandler {
204
205
  metrics.increment('dxos.invitation.expired');
205
206
  await ctx.dispose();
206
207
  },
207
- invitation.created.getTime() + invitation.lifetime * 1000 - Date.now(),
208
+ expiresOn.getTime() - Date.now(),
208
209
  );
209
210
  }
210
211
 
@@ -397,7 +398,7 @@ export class InvitationsHandler {
397
398
  edgeInvitationHandler.handle(ctx, guardedState, protocol, deviceProfile);
398
399
 
399
400
  scheduleTask(ctx, async () => {
400
- const error = protocol.checkInvitation(invitation);
401
+ const error = checkInvitation(protocol, invitation);
401
402
  if (error) {
402
403
  stream.error(error);
403
404
  await ctx.dispose();
@@ -499,6 +500,14 @@ export class InvitationsHandler {
499
500
  }
500
501
  }
501
502
 
503
+ const checkInvitation = (protocol: InvitationProtocol, invitation: Partial<Invitation>) => {
504
+ const expiresOn = getExpirationTime(invitation);
505
+ if (expiresOn && expiresOn.getTime() < Date.now()) {
506
+ return new InvalidInvitationError('Invitation already expired.');
507
+ }
508
+ return protocol.checkInvitation(invitation);
509
+ };
510
+
502
511
  export const createAdmissionKeypair = (): AdmissionKeypair => {
503
512
  const keypair = createKeyPair();
504
513
  return { publicKey: PublicKey.from(keypair.publicKey), privateKey: keypair.secretKey };
@@ -202,7 +202,7 @@ export class InvitationsManager {
202
202
  created = new Date(),
203
203
  guestKeypair = undefined,
204
204
  role = SpaceMember.Role.ADMIN,
205
- lifetime = 86400, // 1 day,
205
+ lifetime = 86400 * 7, // 7 days,
206
206
  multiUse = false,
207
207
  ...options
208
208
  } = _options ?? {};
@@ -28,6 +28,7 @@ import {
28
28
  } from '@dxos/protocols/proto/dxos/halo/invitations';
29
29
 
30
30
  import { type InvitationProtocol } from './invitation-protocol';
31
+ import { computeExpirationTime } from './utils';
31
32
  import { type DataSpaceManager, type SigningContext } from '../spaces';
32
33
 
33
34
  export class SpaceInvitationProtocol implements InvitationProtocol {
@@ -113,9 +114,7 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
113
114
  authMethod: invitation.authMethod,
114
115
  swarmKey: invitation.swarmKey,
115
116
  role: invitation.role ?? SpaceMember.Role.ADMIN,
116
- expiresOn: invitation.lifetime
117
- ? new Date((invitation.created?.getTime() ?? Date.now()) + invitation.lifetime)
118
- : undefined,
117
+ expiresOn: computeExpirationTime(invitation),
119
118
  multiUse: invitation.multiUse ?? false,
120
119
  guestKey:
121
120
  invitation.authMethod === Invitation.AuthMethod.KNOWN_PUBLIC_KEY
@@ -10,6 +10,13 @@ export const stateToString = (state: Invitation.State): string => {
10
10
  return Object.entries(Invitation.State).find(([key, val]) => val === state)?.[0] ?? 'unknown';
11
11
  };
12
12
 
13
+ export const computeExpirationTime = (invitation: Partial<Invitation>): Date | undefined => {
14
+ if (!invitation.lifetime) {
15
+ return;
16
+ }
17
+ return new Date((invitation.created?.getTime() ?? Date.now()) + invitation.lifetime * 1000);
18
+ };
19
+
13
20
  export const tryAcquireBeforeContextDisposed = async (ctx: Context, mutex: Mutex): Promise<MutexGuard> => {
14
21
  let guard: MutexGuard | undefined;
15
22
  return cancelWithContext(
@@ -503,7 +503,7 @@ export class DataSpaceManager extends Resource {
503
503
  });
504
504
  dataSpace.postOpen.append(async () => {
505
505
  const setting = dataSpace.getEdgeReplicationSetting();
506
- if (setting === EdgeReplicationSetting.ENABLED) {
506
+ if (!setting || setting === EdgeReplicationSetting.ENABLED) {
507
507
  await this._echoEdgeReplicator?.connectToSpace(dataSpace.id);
508
508
  } else if (this._echoEdgeReplicator) {
509
509
  log('not connecting EchoEdgeReplicator because of EdgeReplicationSetting', { spaceId: dataSpace.id });
@@ -511,7 +511,7 @@ export class DataSpaceManager extends Resource {
511
511
  });
512
512
  dataSpace.preClose.append(async () => {
513
513
  const setting = dataSpace.getEdgeReplicationSetting();
514
- if (setting === EdgeReplicationSetting.ENABLED) {
514
+ if (!setting || setting === EdgeReplicationSetting.ENABLED) {
515
515
  await this._echoEdgeReplicator?.disconnectFromSpace(dataSpace.id);
516
516
  }
517
517
  });
@@ -609,7 +609,7 @@ export class DataSpaceManager extends Resource {
609
609
  invitationId: invitation.invitationId,
610
610
  swarmKey: invitation.swarmKey,
611
611
  guestKeypair: invitation.guestKey ? { publicKey: invitation.guestKey } : undefined,
612
- lifetime: invitation.expiresOn ? invitation.expiresOn.getTime() - Date.now() : undefined,
612
+ lifetime: invitation.expiresOn ? (invitation.expiresOn.getTime() - Date.now()) / 1000 : undefined,
613
613
  multiUse: invitation.multiUse,
614
614
  delegationCredentialId: credentialId,
615
615
  persistent: false,
@@ -14,7 +14,7 @@ import {
14
14
  type MetadataStore,
15
15
  type Space,
16
16
  } from '@dxos/echo-pipeline';
17
- import { SpaceDocVersion } from '@dxos/echo-protocol';
17
+ import { SpaceDocVersion, type SpaceDoc } from '@dxos/echo-protocol';
18
18
  import type { EdgeConnection, EdgeHttpClient } from '@dxos/edge-client';
19
19
  import { type FeedStore, type FeedWrapper } from '@dxos/feed-store';
20
20
  import { failedInvariant } from '@dxos/invariant';
@@ -446,7 +446,7 @@ export class DataSpace {
446
446
  private _onNewAutomergeRoot(rootUrl: string) {
447
447
  log('loading automerge root doc for space', { space: this.key, rootUrl });
448
448
 
449
- const handle = this._echoHost.automergeRepo.find(rootUrl as any);
449
+ const handle = this._echoHost.automergeRepo.find<SpaceDoc>(rootUrl as any);
450
450
 
451
451
  // TODO(dmaretskyi): Make this single-threaded (but doc loading should still be parallel to not block epoch processing).
452
452
  queueMicrotask(async () => {
@@ -117,7 +117,7 @@ export class EdgeFeedReplicator extends Resource {
117
117
  }
118
118
 
119
119
  async addFeed(feed: FeedWrapper<any>) {
120
- log.info('addFeed', { key: feed.key, connected: this._connected, hasConnectionCtx: !!this._connectionCtx });
120
+ log('addFeed', { key: feed.key, connected: this._connected, hasConnectionCtx: !!this._connectionCtx });
121
121
  this._feeds.set(feed.key, feed);
122
122
 
123
123
  if (this._connected && this._connectionCtx) {
@@ -143,12 +143,12 @@ export class EdgeFeedReplicator extends Resource {
143
143
 
144
144
  private async _sendMessage(message: ProtocolMessage) {
145
145
  if (!this._connectionCtx) {
146
- log.info('message dropped because connection was disposed');
146
+ log('message dropped because connection was disposed');
147
147
  return;
148
148
  }
149
149
 
150
150
  if (message.type === 'data') {
151
- log.info('sending blocks', {
151
+ log('sending blocks', {
152
152
  feedKey: message.feedKey,
153
153
  blocks: message.blocks.map((b) => b.index),
154
154
  });
@@ -191,7 +191,7 @@ export class EdgeFeedReplicator extends Resource {
191
191
 
192
192
  const logMeta = { localLength: feed.length, remoteLength: message.length, feedKey };
193
193
  if (message.length > feed.length) {
194
- log.info('requesting missing blocks', logMeta);
194
+ log('requesting missing blocks', logMeta);
195
195
 
196
196
  await this._sendMessage({
197
197
  type: 'request',
@@ -199,7 +199,7 @@ export class EdgeFeedReplicator extends Resource {
199
199
  range: { from: feed.length, to: message.length },
200
200
  });
201
201
  } else if (message.length < feed.length) {
202
- log.info('pushing blocks to remote', logMeta);
202
+ log('pushing blocks to remote', logMeta);
203
203
 
204
204
  await this._pushBlocks(feed, message.length, feed.length);
205
205
  }
@@ -208,7 +208,7 @@ export class EdgeFeedReplicator extends Resource {
208
208
  }
209
209
 
210
210
  case 'data': {
211
- log.info('received data', { feed: message.feedKey, blocks: message.blocks.map((b) => b.index) });
211
+ log('received data', { feed: message.feedKey, blocks: message.blocks.map((b) => b.index) });
212
212
 
213
213
  const feedKey = PublicKey.fromHex(message.feedKey);
214
214
  const feed = this._feeds.get(feedKey);
@@ -229,7 +229,7 @@ export class EdgeFeedReplicator extends Resource {
229
229
  }
230
230
 
231
231
  private async _pushBlocks(feed: FeedWrapper<any>, from: number, to: number) {
232
- log.info('pushing blocks', { feed: feed.key.toHex(), from, to });
232
+ log('pushing blocks', { feed: feed.key.toHex(), from, to });
233
233
 
234
234
  const blocks: FeedBlock[] = await Promise.all(
235
235
  rangeFromTo(from, to).map(async (index) => {
@@ -255,7 +255,7 @@ export class EdgeFeedReplicator extends Resource {
255
255
  }
256
256
 
257
257
  private async _integrateBlocks(feed: FeedWrapper<any>, blocks: FeedBlock[]) {
258
- log.info('integrating blocks', { feed: feed.key.toHex(), blocks: blocks.length });
258
+ log('integrating blocks', { feed: feed.key.toHex(), blocks: blocks.length });
259
259
 
260
260
  for (const block of blocks) {
261
261
  if (feed.has(block.index)) {
@@ -4,7 +4,12 @@
4
4
 
5
5
  import { EventSubscriptions, UpdateScheduler, scheduleTask } from '@dxos/async';
6
6
  import { Stream } from '@dxos/codec-protobuf/stream';
7
- import { createAdmissionCredentials, type CredentialProcessor, getCredentialAssertion } from '@dxos/credentials';
7
+ import {
8
+ createAdmissionCredentials,
9
+ createDidFromIdentityKey,
10
+ type CredentialProcessor,
11
+ getCredentialAssertion,
12
+ } from '@dxos/credentials';
8
13
  import { raise } from '@dxos/debug';
9
14
  import { type SpaceManager } from '@dxos/echo-pipeline';
10
15
  import { writeMessages } from '@dxos/feed-store';
@@ -115,7 +120,9 @@ export class SpacesServiceImpl implements SpacesService {
115
120
  ctx,
116
121
  async () => {
117
122
  const dataSpaceManager = await this._getDataSpaceManager();
118
- const spaces = Array.from(dataSpaceManager.spaces.values()).map((space) => this._serializeSpace(space));
123
+ const spaces = await Promise.all(
124
+ Array.from(dataSpaceManager.spaces.values()).map((space) => this._serializeSpace(space)),
125
+ );
119
126
  log('update', () => ({ ids: spaces.map((space) => space.id) }));
120
127
  await this._updateMetrics();
121
128
  next({ spaces });
@@ -265,10 +272,10 @@ export class SpacesServiceImpl implements SpacesService {
265
272
  await myIdentity.controlPipeline.writer.write({ credential: { credential } });
266
273
  }
267
274
 
268
- return { space: this._serializeSpace(dataSpace) };
275
+ return { space: await this._serializeSpace(dataSpace) };
269
276
  }
270
277
 
271
- private _serializeSpace(space: DataSpace): Space {
278
+ private async _serializeSpace(space: DataSpace): Promise<Space> {
272
279
  return {
273
280
  id: space.id,
274
281
  spaceKey: space.key,
@@ -291,24 +298,27 @@ export class SpacesServiceImpl implements SpacesService {
291
298
 
292
299
  spaceRootUrl: space.databaseRoot?.url,
293
300
  },
294
- members: Array.from(space.inner.spaceState.members.values()).map((member) => {
295
- const peers = space.presence.getPeersOnline().filter(({ identityKey }) => identityKey.equals(member.key));
296
- const isMe = this._identityManager.identity?.identityKey.equals(member.key);
301
+ members: await Promise.all(
302
+ Array.from(space.inner.spaceState.members.values()).map(async (member) => {
303
+ const peers = space.presence.getPeersOnline().filter(({ identityKey }) => identityKey.equals(member.key));
304
+ const isMe = this._identityManager.identity?.identityKey.equals(member.key);
297
305
 
298
- if (isMe) {
299
- peers.push(space.presence.getLocalState());
300
- }
306
+ if (isMe) {
307
+ peers.push(space.presence.getLocalState());
308
+ }
301
309
 
302
- return {
303
- identity: {
304
- identityKey: member.key,
305
- profile: member.profile ?? {},
306
- },
307
- role: member.role,
308
- presence: peers.length > 0 ? SpaceMember.PresenceState.ONLINE : SpaceMember.PresenceState.OFFLINE,
309
- peerStates: peers,
310
- };
311
- }),
310
+ return {
311
+ identity: {
312
+ did: await createDidFromIdentityKey(member.key),
313
+ identityKey: member.key,
314
+ profile: member.profile ?? {},
315
+ },
316
+ role: member.role,
317
+ presence: peers.length > 0 ? SpaceMember.PresenceState.ONLINE : SpaceMember.PresenceState.OFFLINE,
318
+ peerStates: peers,
319
+ };
320
+ }),
321
+ ),
312
322
  creator: space.inner.spaceState.creator?.key,
313
323
  cache: space.cache,
314
324
  metrics: space.metrics,
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const DXOS_VERSION = "0.7.5-main.9d2a38b";
1
+ export const DXOS_VERSION = "0.7.5-main.e94eead";