@dxos/client-services 0.5.9-main.4ed8273 → 0.5.9-main.72c50cd

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 (39) hide show
  1. package/dist/lib/browser/{chunk-YJ5SPRLY.mjs → chunk-G25WUL6O.mjs} +589 -731
  2. package/dist/lib/browser/chunk-G25WUL6O.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 +9 -10
  6. package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
  7. package/dist/lib/node/{chunk-RQHBQWKH.cjs → chunk-FTGPY2GQ.cjs} +809 -947
  8. package/dist/lib/node/chunk-FTGPY2GQ.cjs.map +7 -0
  9. package/dist/lib/node/index.cjs +42 -42
  10. package/dist/lib/node/meta.json +1 -1
  11. package/dist/lib/node/packlets/testing/index.cjs +15 -16
  12. package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
  13. package/dist/types/src/packlets/identity/identity-service.d.ts +7 -14
  14. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  15. package/dist/types/src/packlets/identity/identity.d.ts +1 -4
  16. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  17. package/dist/types/src/packlets/services/service-host.d.ts +1 -1
  18. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  19. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +3 -5
  20. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  21. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  22. package/dist/types/src/packlets/testing/test-builder.d.ts +6 -8
  23. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  24. package/dist/types/src/version.d.ts +1 -1
  25. package/package.json +36 -36
  26. package/src/packlets/identity/identity-service.test.ts +5 -35
  27. package/src/packlets/identity/identity-service.ts +8 -50
  28. package/src/packlets/identity/identity.ts +2 -25
  29. package/src/packlets/services/service-host.ts +40 -13
  30. package/src/packlets/spaces/data-space-manager.test.ts +1 -46
  31. package/src/packlets/spaces/data-space-manager.ts +25 -54
  32. package/src/packlets/spaces/data-space.ts +5 -10
  33. package/src/packlets/testing/test-builder.ts +6 -11
  34. package/src/version.ts +1 -1
  35. package/dist/lib/browser/chunk-YJ5SPRLY.mjs.map +0 -7
  36. package/dist/lib/node/chunk-RQHBQWKH.cjs.map +0 -7
  37. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +0 -19
  38. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +0 -1
  39. package/src/packlets/identity/default-space-state-machine.ts +0 -44
@@ -24,7 +24,11 @@ describe('IdentityService', () => {
24
24
  beforeEach(async () => {
25
25
  serviceContext = await createServiceContext();
26
26
  await serviceContext.open(new Context());
27
- identityService = createIdentityService(serviceContext);
27
+ identityService = new IdentityServiceImpl(
28
+ (options) => serviceContext.createIdentity(options),
29
+ serviceContext.identityManager,
30
+ serviceContext.keyring,
31
+ );
28
32
  });
29
33
 
30
34
  afterEach(async () => {
@@ -91,37 +95,3 @@ describe('IdentityService', () => {
91
95
  });
92
96
  });
93
97
  });
94
-
95
- describe('open', () => {
96
- test('identity without default space fixed', async () => {
97
- const serviceContext = await createServiceContext();
98
- await serviceContext.open(new Context());
99
- const identity = await serviceContext.createIdentity();
100
- const identityService = createIdentityService(serviceContext);
101
- const getDataSpaces = () => [...(serviceContext.dataSpaceManager?.spaces?.values() ?? [])];
102
- expect(getDataSpaces().length).to.eq(0);
103
- expect(identity.defaultSpaceId).to.be.undefined;
104
- await identityService.open();
105
- expect(getDataSpaces()[0].id === identity.defaultSpaceId).to.be.true;
106
- });
107
-
108
- test('identity without default space credential fixed', async () => {
109
- const serviceContext = await createServiceContext();
110
- await serviceContext.open(new Context());
111
- const identity = await serviceContext.createIdentity();
112
- const space = await serviceContext.dataSpaceManager!.createDefaultSpace();
113
- const identityService = createIdentityService(serviceContext);
114
- expect(identity.defaultSpaceId).to.be.undefined;
115
- await identityService.open();
116
- expect(identity.defaultSpaceId === space.id).to.be.true;
117
- });
118
- });
119
-
120
- const createIdentityService = (serviceContext: ServiceContext) => {
121
- return new IdentityServiceImpl(
122
- serviceContext.identityManager,
123
- serviceContext.keyring,
124
- () => serviceContext.dataSpaceManager!,
125
- (options) => serviceContext.createIdentity(options),
126
- );
127
- };
@@ -3,59 +3,36 @@
3
3
  //
4
4
 
5
5
  import { Stream } from '@dxos/codec-protobuf';
6
- import { Resource } from '@dxos/context';
7
6
  import { signPresentation } from '@dxos/credentials';
8
7
  import { todo } from '@dxos/debug';
9
8
  import { invariant } from '@dxos/invariant';
10
9
  import { type Keyring } from '@dxos/keyring';
11
10
  import {
12
11
  type CreateIdentityRequest,
13
- type Identity as IdentityProto,
12
+ type Identity,
14
13
  type IdentityService,
15
14
  type QueryIdentityResponse,
16
15
  type RecoverIdentityRequest,
17
16
  type SignPresentationRequest,
18
- SpaceState,
19
17
  } from '@dxos/protocols/proto/dxos/client/services';
20
18
  import { type Presentation, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
21
19
 
22
- import { type Identity } from './identity';
23
20
  import { type CreateIdentityOptions, type IdentityManager } from './identity-manager';
24
- import { type DataSpaceManager } from '../spaces';
25
21
 
26
- export class IdentityServiceImpl extends Resource implements IdentityService {
22
+ export class IdentityServiceImpl implements IdentityService {
27
23
  constructor(
24
+ private readonly _createIdentity: (params: CreateIdentityOptions) => Promise<Identity>,
28
25
  private readonly _identityManager: IdentityManager,
29
26
  private readonly _keyring: Keyring,
30
- private readonly _dataSpaceManagerProvider: () => DataSpaceManager,
31
- private readonly _createIdentity: (params: CreateIdentityOptions) => Promise<Identity>,
32
27
  private readonly _onProfileUpdate?: (profile: ProfileDocument | undefined) => Promise<void>,
33
- ) {
34
- super();
35
- }
36
-
37
- protected override async _open() {
38
- const identity = this._identityManager.identity;
39
- if (identity && !identity.defaultSpaceId) {
40
- await this._fixIdentityWithoutDefaultSpace(identity);
41
- }
42
- }
28
+ ) {}
43
29
 
44
- async createIdentity(request: CreateIdentityRequest): Promise<IdentityProto> {
30
+ async createIdentity(request: CreateIdentityRequest): Promise<Identity> {
45
31
  await this._createIdentity({ displayName: request.profile?.displayName, deviceProfile: request.deviceProfile });
46
- const dataSpaceManager = this._dataSpaceManagerProvider();
47
- await this._createDefaultSpace(dataSpaceManager);
48
32
  return this._getIdentity()!;
49
33
  }
50
34
 
51
- private async _createDefaultSpace(dataSpaceManager: DataSpaceManager) {
52
- const space = await dataSpaceManager!.createDefaultSpace();
53
- const identity = this._identityManager.identity;
54
- invariant(identity);
55
- await identity.updateDefaultSpace(space.id);
56
- }
57
-
58
- async recoverIdentity(request: RecoverIdentityRequest): Promise<IdentityProto> {
35
+ async recoverIdentity(request: RecoverIdentityRequest): Promise<Identity> {
59
36
  return todo();
60
37
  }
61
38
 
@@ -68,7 +45,7 @@ export class IdentityServiceImpl extends Resource implements IdentityService {
68
45
  });
69
46
  }
70
47
 
71
- private _getIdentity(): IdentityProto | undefined {
48
+ private _getIdentity(): Identity | undefined {
72
49
  if (!this._identityManager.identity) {
73
50
  return undefined;
74
51
  }
@@ -80,7 +57,7 @@ export class IdentityServiceImpl extends Resource implements IdentityService {
80
57
  };
81
58
  }
82
59
 
83
- async updateProfile(profile: ProfileDocument): Promise<IdentityProto> {
60
+ async updateProfile(profile: ProfileDocument): Promise<Identity> {
84
61
  invariant(this._identityManager.identity, 'Identity not initialized.');
85
62
  await this._identityManager.updateProfile(profile);
86
63
  await this._onProfileUpdate?.(this._identityManager.identity.profileDocument);
@@ -98,23 +75,4 @@ export class IdentityServiceImpl extends Resource implements IdentityService {
98
75
  nonce,
99
76
  });
100
77
  }
101
-
102
- private async _fixIdentityWithoutDefaultSpace(identity: Identity) {
103
- let hasDefaultSpace = false;
104
- const dataSpaceManager = this._dataSpaceManagerProvider();
105
- for (const space of dataSpaceManager.spaces.values()) {
106
- if (space.state === SpaceState.CLOSED) {
107
- await space.open();
108
- await space.initializeDataPipeline();
109
- }
110
- if (await dataSpaceManager.isDefaultSpace(space)) {
111
- await identity.updateDefaultSpace(space.id);
112
- hasDefaultSpace = true;
113
- break;
114
- }
115
- }
116
- if (!hasDefaultSpace) {
117
- await this._createDefaultSpace(dataSpaceManager);
118
- }
119
- }
120
78
  }
@@ -16,7 +16,7 @@ import { type Signer } from '@dxos/crypto';
16
16
  import { type Space } from '@dxos/echo-pipeline';
17
17
  import { writeMessages } from '@dxos/feed-store';
18
18
  import { invariant } from '@dxos/invariant';
19
- import { PublicKey, type SpaceId } from '@dxos/keys';
19
+ import { PublicKey } from '@dxos/keys';
20
20
  import { log } from '@dxos/log';
21
21
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
22
22
  import {
@@ -26,12 +26,10 @@ import {
26
26
  } from '@dxos/protocols/proto/dxos/halo/credentials';
27
27
  import { type DeviceAdmissionRequest } from '@dxos/protocols/proto/dxos/halo/invitations';
28
28
  import { type Presence } from '@dxos/teleport-extension-gossip';
29
- import { Timeframe } from '@dxos/timeframe';
30
29
  import { trace } from '@dxos/tracing';
31
30
  import { type ComplexMap, ComplexSet } from '@dxos/util';
32
31
 
33
32
  import { TrustedKeySetAuthVerifier } from './authenticator';
34
- import { DefaultSpaceStateMachine } from './default-space-state-machine';
35
33
 
36
34
  export type IdentityParams = {
37
35
  identityKey: PublicKey;
@@ -51,7 +49,6 @@ export class Identity {
51
49
  private readonly _presence?: Presence;
52
50
  private readonly _deviceStateMachine: DeviceStateMachine;
53
51
  private readonly _profileStateMachine: ProfileStateMachine;
54
- private readonly _defaultSpaceStateMachine: DefaultSpaceStateMachine;
55
52
  public readonly authVerifier: TrustedKeySetAuthVerifier;
56
53
 
57
54
  public readonly identityKey: PublicKey;
@@ -78,10 +75,6 @@ export class Identity {
78
75
  identityKey: this.identityKey,
79
76
  onUpdate: () => this.stateUpdate.emit(),
80
77
  });
81
- this._defaultSpaceStateMachine = new DefaultSpaceStateMachine({
82
- identityKey: this.identityKey,
83
- onUpdate: () => this.stateUpdate.emit(),
84
- });
85
78
 
86
79
  this.authVerifier = new TrustedKeySetAuthVerifier({
87
80
  trustedKeysProvider: () => new ComplexSet(PublicKey.hash, this.authorizedDeviceKeys.keys()),
@@ -95,24 +88,17 @@ export class Identity {
95
88
  return this._deviceStateMachine.authorizedDeviceKeys;
96
89
  }
97
90
 
98
- get defaultSpaceId(): SpaceId | undefined {
99
- return this._defaultSpaceStateMachine.spaceId;
100
- }
101
-
102
91
  @trace.span()
103
92
  async open(ctx: Context) {
104
- await this._presence?.open();
105
93
  await this.space.spaceState.addCredentialProcessor(this._deviceStateMachine);
106
94
  await this.space.spaceState.addCredentialProcessor(this._profileStateMachine);
107
- await this.space.spaceState.addCredentialProcessor(this._defaultSpaceStateMachine);
108
95
  await this.space.open(ctx);
109
96
  }
110
97
 
111
98
  @trace.span()
112
99
  async close(ctx: Context) {
113
- await this._presence?.close();
100
+ await this._presence?.destroy();
114
101
  await this.authVerifier.close();
115
- await this.space.spaceState.removeCredentialProcessor(this._defaultSpaceStateMachine);
116
102
  await this.space.spaceState.removeCredentialProcessor(this._profileStateMachine);
117
103
  await this.space.spaceState.removeCredentialProcessor(this._deviceStateMachine);
118
104
  await this.space.close();
@@ -171,15 +157,6 @@ export class Identity {
171
157
  return createCredentialSignerWithKey(this._signer, this.deviceKey);
172
158
  }
173
159
 
174
- async updateDefaultSpace(spaceId: SpaceId) {
175
- const credential = await this.getDeviceCredentialSigner().createCredential({
176
- subject: this.identityKey,
177
- assertion: { '@type': 'dxos.halo.credentials.DefaultSpace', spaceId },
178
- });
179
- const receipt = await this.controlPipeline.writer.write({ credential: { credential } });
180
- await this.controlPipeline.state.waitUntilTimeframe(new Timeframe([[receipt.feedKey, receipt.seq]]));
181
- }
182
-
183
160
  async admitDevice({ deviceKey, controlFeedKey, dataFeedKey }: DeviceAdmissionRequest) {
184
161
  log('Admitting device:', {
185
162
  identityKey: this.identityKey,
@@ -3,9 +3,11 @@
3
3
  //
4
4
 
5
5
  import { Event, synchronized } from '@dxos/async';
6
- import { clientServiceBundle, type ClientServices } from '@dxos/client-protocol';
6
+ import { clientServiceBundle, defaultKey, type ClientServices, PropertiesType } from '@dxos/client-protocol';
7
7
  import { type Config } from '@dxos/config';
8
8
  import { Context } from '@dxos/context';
9
+ import { type ObjectStructure, encodeReference, type SpaceDoc } from '@dxos/echo-protocol';
10
+ import { getTypeReference } from '@dxos/echo-schema';
9
11
  import { invariant } from '@dxos/invariant';
10
12
  import { PublicKey } from '@dxos/keys';
11
13
  import { type LevelDB } from '@dxos/kv-store';
@@ -16,6 +18,7 @@ import { trace } from '@dxos/protocols';
16
18
  import { SystemStatus } from '@dxos/protocols/proto/dxos/client/services';
17
19
  import { type Storage } from '@dxos/random-access-storage';
18
20
  import { TRACE_PROCESSOR, trace as Trace } from '@dxos/tracing';
21
+ import { assignDeep } from '@dxos/util';
19
22
  import { WebsocketRpcClient } from '@dxos/websocket-rpc';
20
23
 
21
24
  import { ServiceContext, type ServiceContextRuntimeParams } from './service-context';
@@ -83,7 +86,7 @@ export class ClientServicesHost {
83
86
  private _devtoolsProxy?: WebsocketRpcClient<{}, ClientServices>;
84
87
 
85
88
  private _serviceContext!: ServiceContext;
86
- private readonly _runtimeParams: ServiceContextRuntimeParams;
89
+ private readonly _runtimeParams?: ServiceContextRuntimeParams;
87
90
  private diagnosticsBroadcastHandler: CollectDiagnosticsBroadcastHandler;
88
91
 
89
92
  @Trace.info()
@@ -106,7 +109,7 @@ export class ClientServicesHost {
106
109
  this._storage = storage;
107
110
  this._level = level;
108
111
  this._callbacks = callbacks;
109
- this._runtimeParams = runtimeParams ?? {};
112
+ this._runtimeParams = runtimeParams;
110
113
 
111
114
  if (config) {
112
115
  this.initialize({ config, transportFactory, signalManager });
@@ -251,17 +254,15 @@ export class ClientServicesHost {
251
254
  this._runtimeParams,
252
255
  );
253
256
 
254
- const identityService = new IdentityServiceImpl(
255
- this._serviceContext.identityManager,
256
- this._serviceContext.keyring,
257
- () => this._serviceContext.dataSpaceManager!,
258
- (params) => this._createIdentity(params),
259
- (profile) => this._serviceContext.broadcastProfileUpdate(profile),
260
- );
261
-
262
257
  this._serviceRegistry.setServices({
263
258
  SystemService: this._systemService,
264
- IdentityService: identityService,
259
+
260
+ IdentityService: new IdentityServiceImpl(
261
+ (params) => this._createIdentity(params),
262
+ this._serviceContext.identityManager,
263
+ this._serviceContext.keyring,
264
+ (profile) => this._serviceContext.broadcastProfileUpdate(profile),
265
+ ),
265
266
 
266
267
  InvitationsService: new InvitationsServiceImpl(this._serviceContext.invitationsManager),
267
268
 
@@ -293,7 +294,6 @@ export class ClientServicesHost {
293
294
  });
294
295
 
295
296
  await this._serviceContext.open(ctx);
296
- await identityService.open();
297
297
 
298
298
  const devtoolsProxy = this._config?.get('runtime.client.devtoolsProxy');
299
299
  if (devtoolsProxy) {
@@ -349,7 +349,34 @@ export class ClientServicesHost {
349
349
 
350
350
  private async _createIdentity(params: CreateIdentityOptions) {
351
351
  const identity = await this._serviceContext.createIdentity(params);
352
+
353
+ // Setup default space.
352
354
  await this._serviceContext.initialized.wait();
355
+ const space = await this._serviceContext.dataSpaceManager!.createSpace();
356
+
357
+ const automergeIndex = space.automergeSpaceState.rootUrl;
358
+ invariant(automergeIndex);
359
+ const document = this._serviceContext.echoHost.automergeRepo.find<SpaceDoc>(automergeIndex as any);
360
+ await document.whenReady();
361
+
362
+ // TODO(dmaretskyi): Better API for low-level data access.
363
+ const properties: ObjectStructure = {
364
+ system: {
365
+ type: encodeReference(getTypeReference(PropertiesType)!),
366
+ },
367
+ data: {
368
+ [defaultKey]: identity.identityKey.toHex(),
369
+ },
370
+ meta: {
371
+ keys: [],
372
+ },
373
+ };
374
+ const propertiesId = PublicKey.random().toHex();
375
+ document.change((doc: SpaceDoc) => {
376
+ assignDeep(doc, ['objects', propertiesId], properties);
377
+ });
378
+
379
+ await this._serviceContext.echoHost.flush();
353
380
  return identity;
354
381
  }
355
382
  }
@@ -12,7 +12,7 @@ import { log } from '@dxos/log';
12
12
  import { SpaceState } from '@dxos/protocols/proto/dxos/client/services';
13
13
  import { describe, openAndClose, test } from '@dxos/test';
14
14
 
15
- import { TestBuilder, type TestPeer } from '../testing';
15
+ import { TestBuilder } from '../testing';
16
16
 
17
17
  describe('DataSpaceManager', () => {
18
18
  test('create space', async () => {
@@ -168,50 +168,5 @@ describe('DataSpaceManager', () => {
168
168
  500,
169
169
  );
170
170
  });
171
-
172
- test('activate opens a lazily loaded space', async () => {
173
- const builder = new TestBuilder();
174
-
175
- const peer = builder.createPeer();
176
- await peer.createIdentity();
177
- await openAndClose(peer.echoHost, peer.dataSpaceManager);
178
-
179
- await peer.dataSpaceManager.createSpace();
180
- await reloadDataSpaces(peer);
181
-
182
- const space = getFirstSpace(peer);
183
- expect(space.state).to.equal(SpaceState.CLOSED);
184
- await space.activate();
185
- await asyncTimeout(
186
- space.stateUpdate.waitForCondition(() => space.state === SpaceState.READY),
187
- 500,
188
- );
189
- });
190
-
191
- test('deactivate lazily loaded space ', async () => {
192
- const builder = new TestBuilder();
193
-
194
- const peer = builder.createPeer();
195
- await peer.createIdentity();
196
- await openAndClose(peer.echoHost, peer.dataSpaceManager);
197
-
198
- await peer.dataSpaceManager.createSpace();
199
- await reloadDataSpaces(peer);
200
-
201
- await getFirstSpace(peer).deactivate();
202
-
203
- await reloadDataSpaces(peer);
204
-
205
- expect(getFirstSpace(peer).state).to.eq(SpaceState.INACTIVE);
206
- });
207
171
  });
208
-
209
- const reloadDataSpaces = async (peer: TestPeer) => {
210
- await peer.dataSpaceManager.close();
211
- await peer.dataSpaceManager.open();
212
- };
213
-
214
- const getFirstSpace = (peer: TestPeer) => {
215
- return Array.from(peer.dataSpaceManager.spaces.values())[0];
216
- };
217
172
  });
@@ -4,8 +4,7 @@
4
4
 
5
5
  import { Event, synchronized, trackLeaks } from '@dxos/async';
6
6
  import { type Doc } from '@dxos/automerge/automerge';
7
- import { type DocHandle, type AutomergeUrl } from '@dxos/automerge/automerge-repo';
8
- import { PropertiesType } from '@dxos/client-protocol';
7
+ import { type AutomergeUrl } from '@dxos/automerge/automerge-repo';
9
8
  import { cancelWithContext, Context } from '@dxos/context';
10
9
  import {
11
10
  type CredentialSigner,
@@ -22,8 +21,7 @@ import {
22
21
  type SpaceProtocol,
23
22
  type SpaceProtocolSession,
24
23
  } from '@dxos/echo-pipeline';
25
- import { encodeReference, type ObjectStructure, type SpaceDoc } from '@dxos/echo-protocol';
26
- import { getTypeReference } from '@dxos/echo-schema';
24
+ import { type SpaceDoc } from '@dxos/echo-protocol';
27
25
  import { type FeedStore } from '@dxos/feed-store';
28
26
  import { invariant } from '@dxos/invariant';
29
27
  import { type Keyring } from '@dxos/keyring';
@@ -39,7 +37,7 @@ import { type PeerState } from '@dxos/protocols/proto/dxos/mesh/presence';
39
37
  import { Gossip, Presence } from '@dxos/teleport-extension-gossip';
40
38
  import { type Timeframe } from '@dxos/timeframe';
41
39
  import { trace } from '@dxos/tracing';
42
- import { assignDeep, ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
40
+ import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
43
41
 
44
42
  import { DataSpace, findPropertiesObject } from './data-space';
45
43
  import { spaceGenesis } from './genesis';
@@ -49,9 +47,6 @@ import { type InvitationsManager } from '../invitations';
49
47
  const PRESENCE_ANNOUNCE_INTERVAL = 10_000;
50
48
  const PRESENCE_OFFLINE_TIMEOUT = 20_000;
51
49
 
52
- // Space properties key for default metadata.
53
- const DEFAULT_SPACE_KEY = '__DEFAULT__';
54
-
55
50
  export interface SigningContext {
56
51
  identityKey: PublicKey;
57
52
  deviceKey: PublicKey;
@@ -93,6 +88,8 @@ export class DataSpaceManager {
93
88
 
94
89
  private _isOpen = false;
95
90
  private readonly _instanceId = PublicKey.random().toHex();
91
+ private readonly _spaceMemberPresenceAnnounceInterval: number;
92
+ private readonly _spaceMemberPresenceOfflineTimeout: number;
96
93
 
97
94
  constructor(
98
95
  private readonly _spaceManager: SpaceManager,
@@ -102,8 +99,15 @@ export class DataSpaceManager {
102
99
  private readonly _feedStore: FeedStore<FeedMessage>,
103
100
  private readonly _echoHost: EchoHost,
104
101
  private readonly _invitationsManager: InvitationsManager,
105
- private readonly _params?: DataSpaceManagerRuntimeParams,
102
+ params?: DataSpaceManagerRuntimeParams,
106
103
  ) {
104
+ const {
105
+ spaceMemberPresenceAnnounceInterval = PRESENCE_ANNOUNCE_INTERVAL,
106
+ spaceMemberPresenceOfflineTimeout = PRESENCE_OFFLINE_TIMEOUT,
107
+ } = params ?? {};
108
+ this._spaceMemberPresenceAnnounceInterval = spaceMemberPresenceAnnounceInterval;
109
+ this._spaceMemberPresenceOfflineTimeout = spaceMemberPresenceOfflineTimeout;
110
+
107
111
  trace.diagnostic({
108
112
  id: 'spaces',
109
113
  name: 'Spaces',
@@ -153,6 +157,12 @@ export class DataSpaceManager {
153
157
  this._isOpen = true;
154
158
  this.updated.emit();
155
159
 
160
+ for (const space of this._spaces.values()) {
161
+ if (space.state !== SpaceState.INACTIVE) {
162
+ space.initializeDataPipelineAsync();
163
+ }
164
+ }
165
+
156
166
  log.trace('dxos.echo.data-space-manager.open', Trace.end({ id: this._instanceId }));
157
167
  }
158
168
 
@@ -164,7 +174,6 @@ export class DataSpaceManager {
164
174
  for (const space of this._spaces.values()) {
165
175
  await space.close();
166
176
  }
167
- this._spaces.clear();
168
177
  }
169
178
 
170
179
  /**
@@ -188,7 +197,6 @@ export class DataSpaceManager {
188
197
 
189
198
  const root = await this._echoHost.createSpaceRoot(spaceKey);
190
199
  const space = await this._constructSpace(metadata);
191
- await space.open();
192
200
 
193
201
  const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner, root.url);
194
202
  await this._metadataStore.addSpace(metadata);
@@ -203,46 +211,6 @@ export class DataSpaceManager {
203
211
  return space;
204
212
  }
205
213
 
206
- async isDefaultSpace(space: DataSpace): Promise<boolean> {
207
- const rootDoc = await this._getSpaceRootDocument(space);
208
- const [_, properties] = findPropertiesObject(rootDoc.docSync()) ?? [];
209
- return properties?.data?.[DEFAULT_SPACE_KEY] === this._signingContext.identityKey.toHex();
210
- }
211
-
212
- async createDefaultSpace() {
213
- const space = await this.createSpace();
214
- const document = await this._getSpaceRootDocument(space);
215
-
216
- // TODO(dmaretskyi): Better API for low-level data access.
217
- const properties: ObjectStructure = {
218
- system: {
219
- type: encodeReference(getTypeReference(PropertiesType)!),
220
- },
221
- data: {
222
- [DEFAULT_SPACE_KEY]: this._signingContext.identityKey.toHex(),
223
- },
224
- meta: {
225
- keys: [],
226
- },
227
- };
228
-
229
- const propertiesId = PublicKey.random().toHex();
230
- document.change((doc: SpaceDoc) => {
231
- assignDeep(doc, ['objects', propertiesId], properties);
232
- });
233
-
234
- await this._echoHost.flush();
235
- return space;
236
- }
237
-
238
- private async _getSpaceRootDocument(space: DataSpace): Promise<DocHandle<SpaceDoc>> {
239
- const automergeIndex = space.automergeSpaceState.rootUrl;
240
- invariant(automergeIndex);
241
- const document = this._echoHost.automergeRepo.find<SpaceDoc>(automergeIndex as any);
242
- await document.whenReady();
243
- return document;
244
- }
245
-
246
214
  // TODO(burdon): Rename join space.
247
215
  @synchronized
248
216
  async acceptSpace(opts: AcceptSpaceOptions): Promise<DataSpace> {
@@ -258,7 +226,6 @@ export class DataSpaceManager {
258
226
  };
259
227
 
260
228
  const space = await this._constructSpace(metadata);
261
- await space.open();
262
229
  await this._metadataStore.addSpace(metadata);
263
230
  space.initializeDataPipelineAsync();
264
231
 
@@ -287,8 +254,8 @@ export class DataSpaceManager {
287
254
  localPeerId: this._signingContext.deviceKey,
288
255
  });
289
256
  const presence = new Presence({
290
- announceInterval: this._params?.spaceMemberPresenceAnnounceInterval ?? PRESENCE_ANNOUNCE_INTERVAL,
291
- offlineTimeout: this._params?.spaceMemberPresenceOfflineTimeout ?? PRESENCE_OFFLINE_TIMEOUT,
257
+ announceInterval: this._spaceMemberPresenceAnnounceInterval,
258
+ offlineTimeout: this._spaceMemberPresenceOfflineTimeout,
292
259
  identityKey: this._signingContext.identityKey,
293
260
  gossip,
294
261
  });
@@ -369,6 +336,10 @@ export class DataSpaceManager {
369
336
  }
370
337
  });
371
338
 
339
+ if (metadata.state !== SpaceState.INACTIVE) {
340
+ await dataSpace.open();
341
+ }
342
+
372
343
  if (metadata.controlTimeframe) {
373
344
  dataSpace.inner.controlPipeline.state.setTargetTimeframe(metadata.controlTimeframe);
374
345
  }
@@ -193,13 +193,10 @@ export class DataSpace {
193
193
 
194
194
  @synchronized
195
195
  async open() {
196
- if (this._state === SpaceState.CLOSED) {
197
- await this._open();
198
- }
196
+ await this._open();
199
197
  }
200
198
 
201
199
  private async _open() {
202
- await this._presence.open();
203
200
  await this._gossip.open();
204
201
  await this._notarizationPlugin.open();
205
202
  await this._inner.spaceState.addCredentialProcessor(this._notarizationPlugin);
@@ -231,7 +228,7 @@ export class DataSpace {
231
228
  await this._inner.spaceState.removeCredentialProcessor(this._notarizationPlugin);
232
229
  await this._notarizationPlugin.close();
233
230
 
234
- await this._presence.close();
231
+ await this._presence.destroy();
235
232
  await this._gossip.close();
236
233
  }
237
234
 
@@ -456,7 +453,6 @@ export class DataSpace {
456
453
  const rootHandle = this._echoHost.automergeRepo.find(currentRootUrl as any);
457
454
  await cancelWithContext(this._ctx, asyncTimeout(rootHandle.whenReady(), 10_000));
458
455
  const newRoot = this._echoHost.automergeRepo.create(rootHandle.docSync());
459
- await this._echoHost.automergeRepo.flush([newRoot.documentId]);
460
456
  invariant(typeof newRoot.url === 'string' && newRoot.url.length > 0);
461
457
  // TODO(dmaretskyi): Unify epoch construction.
462
458
  epoch = {
@@ -548,7 +544,7 @@ export class DataSpace {
548
544
 
549
545
  @synchronized
550
546
  async activate() {
551
- if (![SpaceState.CLOSED, SpaceState.INACTIVE].includes(this._state)) {
547
+ if (this._state !== SpaceState.INACTIVE) {
552
548
  return;
553
549
  }
554
550
 
@@ -562,11 +558,10 @@ export class DataSpace {
562
558
  if (this._state === SpaceState.INACTIVE) {
563
559
  return;
564
560
  }
561
+
565
562
  // Unregister from data service.
566
563
  await this._metadataStore.setSpaceState(this.key, SpaceState.INACTIVE);
567
- if (this._state !== SpaceState.CLOSED) {
568
- await this._close();
569
- }
564
+ await this._close();
570
565
  this._state = SpaceState.INACTIVE;
571
566
  log('new state', { state: SpaceState[this._state] });
572
567
  this.stateUpdate.emit();