@dxos/client-services 0.5.8 → 0.5.9-main.079a532

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 (90) hide show
  1. package/dist/lib/browser/{chunk-JECXTGZA.mjs → chunk-QZ5LNV55.mjs} +1781 -1480
  2. package/dist/lib/browser/chunk-QZ5LNV55.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +55 -365
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/packlets/testing/index.mjs +14 -14
  7. package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/{chunk-OR7LRWDY.cjs → chunk-V7UMWFUS.cjs} +2055 -1761
  9. package/dist/lib/node/chunk-V7UMWFUS.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +92 -397
  11. package/dist/lib/node/index.cjs.map +4 -4
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/packlets/testing/index.cjs +19 -19
  14. package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
  15. package/dist/types/src/index.d.ts +3 -3
  16. package/dist/types/src/index.d.ts.map +1 -1
  17. package/dist/types/src/packlets/devtools/network.d.ts +4 -4
  18. package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
  19. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +19 -0
  20. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +1 -0
  21. package/dist/types/src/packlets/identity/identity-service.d.ts +14 -7
  22. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  23. package/dist/types/src/packlets/identity/identity.d.ts +4 -1
  24. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  25. package/dist/types/src/packlets/network/network-service.d.ts +2 -2
  26. package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
  27. package/dist/types/src/packlets/services/service-context.d.ts +4 -5
  28. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  29. package/dist/types/src/packlets/services/service-host.d.ts +1 -1
  30. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  31. package/dist/types/src/packlets/spaces/automerge-space-state.d.ts +4 -1
  32. package/dist/types/src/packlets/spaces/automerge-space-state.d.ts.map +1 -1
  33. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +5 -3
  34. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  35. package/dist/types/src/packlets/spaces/data-space.d.ts +11 -9
  36. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  37. package/dist/types/src/packlets/spaces/epoch-migrations.d.ts +23 -0
  38. package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -0
  39. package/dist/types/src/packlets/spaces/spaces-service.d.ts +2 -2
  40. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  41. package/dist/types/src/packlets/testing/test-builder.d.ts +11 -9
  42. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  43. package/dist/types/src/packlets/worker/index.d.ts +3 -0
  44. package/dist/types/src/packlets/worker/index.d.ts.map +1 -0
  45. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -0
  46. package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -0
  47. package/dist/types/src/version.d.ts +1 -1
  48. package/dist/types/src/version.d.ts.map +1 -1
  49. package/package.json +36 -36
  50. package/src/index.ts +3 -3
  51. package/src/packlets/devtools/network.ts +4 -4
  52. package/src/packlets/identity/default-space-state-machine.ts +44 -0
  53. package/src/packlets/identity/identity-manager.test.ts +2 -2
  54. package/src/packlets/identity/identity-service.test.ts +35 -5
  55. package/src/packlets/identity/identity-service.ts +55 -8
  56. package/src/packlets/identity/identity.test.ts +8 -4
  57. package/src/packlets/identity/identity.ts +25 -2
  58. package/src/packlets/invitations/invitations-handler.ts +2 -2
  59. package/src/packlets/network/network-service.ts +2 -2
  60. package/src/packlets/services/service-context.ts +4 -7
  61. package/src/packlets/services/service-host.ts +16 -44
  62. package/src/packlets/spaces/automerge-space-state.ts +11 -2
  63. package/src/packlets/spaces/data-space-manager.test.ts +46 -1
  64. package/src/packlets/spaces/data-space-manager.ts +81 -31
  65. package/src/packlets/spaces/data-space.ts +89 -132
  66. package/src/packlets/spaces/epoch-migrations.ts +135 -0
  67. package/src/packlets/spaces/spaces-service.ts +5 -2
  68. package/src/packlets/testing/test-builder.ts +16 -14
  69. package/src/packlets/worker/index.ts +6 -0
  70. package/src/version.ts +1 -5
  71. package/dist/lib/browser/chunk-JECXTGZA.mjs.map +0 -7
  72. package/dist/lib/node/chunk-OR7LRWDY.cjs.map +0 -7
  73. package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts +0 -37
  74. package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts.map +0 -1
  75. package/dist/types/src/packlets/vault/index.d.ts +0 -6
  76. package/dist/types/src/packlets/vault/index.d.ts.map +0 -1
  77. package/dist/types/src/packlets/vault/shared-worker-connection.d.ts +0 -36
  78. package/dist/types/src/packlets/vault/shared-worker-connection.d.ts.map +0 -1
  79. package/dist/types/src/packlets/vault/shell-runtime.d.ts +0 -33
  80. package/dist/types/src/packlets/vault/shell-runtime.d.ts.map +0 -1
  81. package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +0 -1
  82. package/dist/types/src/packlets/vault/worker-session.d.ts.map +0 -1
  83. package/src/packlets/vault/iframe-host-runtime.ts +0 -130
  84. package/src/packlets/vault/index.ts +0 -9
  85. package/src/packlets/vault/shared-worker-connection.ts +0 -115
  86. package/src/packlets/vault/shell-runtime.ts +0 -111
  87. /package/dist/types/src/packlets/{vault → worker}/worker-runtime.d.ts +0 -0
  88. /package/dist/types/src/packlets/{vault → worker}/worker-session.d.ts +0 -0
  89. /package/src/packlets/{vault → worker}/worker-runtime.ts +0 -0
  90. /package/src/packlets/{vault → worker}/worker-session.ts +0 -0
@@ -7,6 +7,7 @@ import expect from 'expect';
7
7
  import { Context } from '@dxos/context';
8
8
  import { CredentialGenerator, verifyCredential } from '@dxos/credentials';
9
9
  import {
10
+ createIdFromSpaceKey,
10
11
  MetadataStore,
11
12
  MOCK_AUTH_PROVIDER,
12
13
  MOCK_AUTH_VERIFIER,
@@ -20,7 +21,7 @@ import { FeedFactory, FeedStore } from '@dxos/feed-store';
20
21
  import { Keyring } from '@dxos/keyring';
21
22
  import { type PublicKey } from '@dxos/keys';
22
23
  import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
23
- import { MemoryTransportFactory, NetworkManager } from '@dxos/network-manager';
24
+ import { MemoryTransportFactory, SwarmNetworkManager } from '@dxos/network-manager';
24
25
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
25
26
  import { AdmittedFeed } from '@dxos/protocols/proto/dxos/halo/credentials';
26
27
  import { createStorage, StorageType } from '@dxos/random-access-storage';
@@ -78,7 +79,7 @@ describe('identity/identity', () => {
78
79
  credentialAuthenticator: MOCK_AUTH_VERIFIER,
79
80
  },
80
81
  blobStore,
81
- networkManager: new NetworkManager({
82
+ networkManager: new SwarmNetworkManager({
82
83
  signalManager: new MemorySignalManager(new MemorySignalManagerContext()),
83
84
  transportFactory: MemoryTransportFactory,
84
85
  }),
@@ -86,6 +87,7 @@ describe('identity/identity', () => {
86
87
 
87
88
  await metadataStore.setIdentityRecord({ haloSpace: { key: spaceKey }, identityKey, deviceKey });
88
89
  const space: Space = new Space({
90
+ id: await createIdFromSpaceKey(spaceKey),
89
91
  spaceKey,
90
92
  protocol,
91
93
  genesisFeed: controlFeed,
@@ -193,7 +195,7 @@ describe('identity/identity', () => {
193
195
  credentialAuthenticator: MOCK_AUTH_VERIFIER, // createHaloAuthVerifier(() => identity.authorizedDeviceKeys),
194
196
  },
195
197
  blobStore,
196
- networkManager: new NetworkManager({
198
+ networkManager: new SwarmNetworkManager({
197
199
  signalManager: new MemorySignalManager(signalContext),
198
200
  transportFactory: MemoryTransportFactory,
199
201
  }),
@@ -201,6 +203,7 @@ describe('identity/identity', () => {
201
203
 
202
204
  await metadataStore.setIdentityRecord({ haloSpace: { key: spaceKey }, identityKey, deviceKey });
203
205
  const space = new Space({
206
+ id: await createIdFromSpaceKey(spaceKey),
204
207
  spaceKey,
205
208
  protocol,
206
209
  genesisFeed: controlFeed,
@@ -281,7 +284,7 @@ describe('identity/identity', () => {
281
284
  credentialAuthenticator: MOCK_AUTH_VERIFIER, // createHaloAuthVerifier(() => identity.authorizedDeviceKeys),
282
285
  },
283
286
  blobStore,
284
- networkManager: new NetworkManager({
287
+ networkManager: new SwarmNetworkManager({
285
288
  signalManager: new MemorySignalManager(signalContext),
286
289
  transportFactory: MemoryTransportFactory,
287
290
  }),
@@ -293,6 +296,7 @@ describe('identity/identity', () => {
293
296
  deviceKey,
294
297
  });
295
298
  const space = new Space({
299
+ id: await createIdFromSpaceKey(spaceKey),
296
300
  spaceKey,
297
301
  protocol,
298
302
  genesisFeed: await feedStore.openFeed(genesisFeedKey),
@@ -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 } from '@dxos/keys';
19
+ import { PublicKey, type SpaceId } 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,10 +26,12 @@ 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';
29
30
  import { trace } from '@dxos/tracing';
30
31
  import { type ComplexMap, ComplexSet } from '@dxos/util';
31
32
 
32
33
  import { TrustedKeySetAuthVerifier } from './authenticator';
34
+ import { DefaultSpaceStateMachine } from './default-space-state-machine';
33
35
 
34
36
  export type IdentityParams = {
35
37
  identityKey: PublicKey;
@@ -49,6 +51,7 @@ export class Identity {
49
51
  private readonly _presence?: Presence;
50
52
  private readonly _deviceStateMachine: DeviceStateMachine;
51
53
  private readonly _profileStateMachine: ProfileStateMachine;
54
+ private readonly _defaultSpaceStateMachine: DefaultSpaceStateMachine;
52
55
  public readonly authVerifier: TrustedKeySetAuthVerifier;
53
56
 
54
57
  public readonly identityKey: PublicKey;
@@ -75,6 +78,10 @@ export class Identity {
75
78
  identityKey: this.identityKey,
76
79
  onUpdate: () => this.stateUpdate.emit(),
77
80
  });
81
+ this._defaultSpaceStateMachine = new DefaultSpaceStateMachine({
82
+ identityKey: this.identityKey,
83
+ onUpdate: () => this.stateUpdate.emit(),
84
+ });
78
85
 
79
86
  this.authVerifier = new TrustedKeySetAuthVerifier({
80
87
  trustedKeysProvider: () => new ComplexSet(PublicKey.hash, this.authorizedDeviceKeys.keys()),
@@ -88,17 +95,24 @@ export class Identity {
88
95
  return this._deviceStateMachine.authorizedDeviceKeys;
89
96
  }
90
97
 
98
+ get defaultSpaceId(): SpaceId | undefined {
99
+ return this._defaultSpaceStateMachine.spaceId;
100
+ }
101
+
91
102
  @trace.span()
92
103
  async open(ctx: Context) {
104
+ await this._presence?.open();
93
105
  await this.space.spaceState.addCredentialProcessor(this._deviceStateMachine);
94
106
  await this.space.spaceState.addCredentialProcessor(this._profileStateMachine);
107
+ await this.space.spaceState.addCredentialProcessor(this._defaultSpaceStateMachine);
95
108
  await this.space.open(ctx);
96
109
  }
97
110
 
98
111
  @trace.span()
99
112
  async close(ctx: Context) {
100
- await this._presence?.destroy();
113
+ await this._presence?.close();
101
114
  await this.authVerifier.close();
115
+ await this.space.spaceState.removeCredentialProcessor(this._defaultSpaceStateMachine);
102
116
  await this.space.spaceState.removeCredentialProcessor(this._profileStateMachine);
103
117
  await this.space.spaceState.removeCredentialProcessor(this._deviceStateMachine);
104
118
  await this.space.close();
@@ -157,6 +171,15 @@ export class Identity {
157
171
  return createCredentialSignerWithKey(this._signer, this.deviceKey);
158
172
  }
159
173
 
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
+
160
183
  async admitDevice({ deviceKey, controlFeedKey, dataFeedKey }: DeviceAdmissionRequest) {
161
184
  log('Admitting device:', {
162
185
  identityKey: this.identityKey,
@@ -9,7 +9,7 @@ import { createKeyPair, sign } from '@dxos/crypto';
9
9
  import { invariant } from '@dxos/invariant';
10
10
  import { PublicKey } from '@dxos/keys';
11
11
  import { log } from '@dxos/log';
12
- import { createTeleportProtocolFactory, type NetworkManager, type SwarmConnection } from '@dxos/network-manager';
12
+ import { createTeleportProtocolFactory, type SwarmNetworkManager, type SwarmConnection } from '@dxos/network-manager';
13
13
  import { InvalidInvitationExtensionRoleError, trace } from '@dxos/protocols';
14
14
  import { type AdmissionKeypair, Invitation } from '@dxos/protocols/proto/dxos/client/services';
15
15
  import { type DeviceProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
@@ -61,7 +61,7 @@ export class InvitationsHandler {
61
61
  * @internal
62
62
  */
63
63
  constructor(
64
- private readonly _networkManager: NetworkManager,
64
+ private readonly _networkManager: SwarmNetworkManager,
65
65
  private readonly _defaultTeleportParams?: Partial<TeleportParams>,
66
66
  ) {}
67
67
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { Stream } from '@dxos/codec-protobuf';
6
6
  import { type SignalManager } from '@dxos/messaging';
7
- import { type NetworkManager } from '@dxos/network-manager';
7
+ import { type SwarmNetworkManager } from '@dxos/network-manager';
8
8
  import {
9
9
  type NetworkService,
10
10
  type NetworkStatus,
@@ -13,7 +13,7 @@ import {
13
13
 
14
14
  export class NetworkServiceImpl implements NetworkService {
15
15
  constructor(
16
- private readonly networkManager: NetworkManager,
16
+ private readonly networkManager: SwarmNetworkManager,
17
17
  private readonly signalManager: SignalManager,
18
18
  ) {}
19
19
 
@@ -15,7 +15,7 @@ import { PublicKey } from '@dxos/keys';
15
15
  import { type LevelDB } from '@dxos/kv-store';
16
16
  import { log } from '@dxos/log';
17
17
  import { type SignalManager } from '@dxos/messaging';
18
- import { type NetworkManager } from '@dxos/network-manager';
18
+ import { type SwarmNetworkManager } from '@dxos/network-manager';
19
19
  import { InvalidStorageVersionError, STORAGE_VERSION, trace } from '@dxos/protocols';
20
20
  import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
21
21
  import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
@@ -35,10 +35,10 @@ import {
35
35
  import {
36
36
  DeviceInvitationProtocol,
37
37
  InvitationsHandler,
38
+ InvitationsManager,
38
39
  SpaceInvitationProtocol,
39
40
  type InvitationProtocol,
40
41
  } from '../invitations';
41
- import { InvitationsManager } from '../invitations/invitations-manager';
42
42
  import { DataSpaceManager, type DataSpaceManagerRuntimeParams, type SigningContext } from '../spaces';
43
43
 
44
44
  export type ServiceContextRuntimeParams = IdentityManagerRuntimeParams &
@@ -81,7 +81,7 @@ export class ServiceContext extends Resource {
81
81
  constructor(
82
82
  public readonly storage: Storage,
83
83
  public readonly level: LevelDB,
84
- public readonly networkManager: NetworkManager,
84
+ public readonly networkManager: SwarmNetworkManager,
85
85
  public readonly signalManager: SignalManager,
86
86
  public readonly _runtimeParams?: ServiceContextRuntimeParams,
87
87
  ) {
@@ -120,10 +120,7 @@ export class ServiceContext extends Resource {
120
120
  this._runtimeParams as IdentityManagerRuntimeParams,
121
121
  );
122
122
 
123
- this.echoHost = new EchoHost({
124
- kv: this.level,
125
- storage: this.storage,
126
- });
123
+ this.echoHost = new EchoHost({ kv: this.level });
127
124
 
128
125
  this.invitations = new InvitationsHandler(this.networkManager, _runtimeParams?.invitationConnectionDefaultParams);
129
126
  this.invitationsManager = new InvitationsManager(
@@ -3,22 +3,19 @@
3
3
  //
4
4
 
5
5
  import { Event, synchronized } from '@dxos/async';
6
- import { clientServiceBundle, defaultKey, type ClientServices, Properties } from '@dxos/client-protocol';
6
+ import { clientServiceBundle, type ClientServices } 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';
11
9
  import { invariant } from '@dxos/invariant';
12
10
  import { PublicKey } from '@dxos/keys';
13
11
  import { type LevelDB } from '@dxos/kv-store';
14
12
  import { log } from '@dxos/log';
15
13
  import { WebsocketSignalManager, type SignalManager } from '@dxos/messaging';
16
- import { NetworkManager, createSimplePeerTransportFactory, type TransportFactory } from '@dxos/network-manager';
14
+ import { SwarmNetworkManager, createSimplePeerTransportFactory, type TransportFactory } from '@dxos/network-manager';
17
15
  import { trace } from '@dxos/protocols';
18
16
  import { SystemStatus } from '@dxos/protocols/proto/dxos/client/services';
19
17
  import { type Storage } from '@dxos/random-access-storage';
20
18
  import { TRACE_PROCESSOR, trace as Trace } from '@dxos/tracing';
21
- import { assignDeep } from '@dxos/util';
22
19
  import { WebsocketRpcClient } from '@dxos/websocket-rpc';
23
20
 
24
21
  import { ServiceContext, type ServiceContextRuntimeParams } from './service-context';
@@ -79,14 +76,14 @@ export class ClientServicesHost {
79
76
  private _config?: Config;
80
77
  private readonly _statusUpdate = new Event<void>();
81
78
  private _signalManager?: SignalManager;
82
- private _networkManager?: NetworkManager;
79
+ private _networkManager?: SwarmNetworkManager;
83
80
  private _storage?: Storage;
84
81
  private _level?: LevelDB;
85
82
  private _callbacks?: ClientServicesHostCallbacks;
86
83
  private _devtoolsProxy?: WebsocketRpcClient<{}, ClientServices>;
87
84
 
88
85
  private _serviceContext!: ServiceContext;
89
- private readonly _runtimeParams?: ServiceContextRuntimeParams;
86
+ private readonly _runtimeParams: ServiceContextRuntimeParams;
90
87
  private diagnosticsBroadcastHandler: CollectDiagnosticsBroadcastHandler;
91
88
 
92
89
  @Trace.info()
@@ -109,7 +106,7 @@ export class ClientServicesHost {
109
106
  this._storage = storage;
110
107
  this._level = level;
111
108
  this._callbacks = callbacks;
112
- this._runtimeParams = runtimeParams;
109
+ this._runtimeParams = runtimeParams ?? {};
113
110
 
114
111
  if (config) {
115
112
  this.initialize({ config, transportFactory, signalManager });
@@ -210,7 +207,7 @@ export class ClientServicesHost {
210
207
  this._signalManager = signalManager;
211
208
 
212
209
  invariant(!this._networkManager, 'network manager already set');
213
- this._networkManager = new NetworkManager({
210
+ this._networkManager = new SwarmNetworkManager({
214
211
  log: connectionLog,
215
212
  transportFactory,
216
213
  signalManager,
@@ -254,15 +251,17 @@ export class ClientServicesHost {
254
251
  this._runtimeParams,
255
252
  );
256
253
 
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
+
257
262
  this._serviceRegistry.setServices({
258
263
  SystemService: this._systemService,
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
- ),
264
+ IdentityService: identityService,
266
265
 
267
266
  InvitationsService: new InvitationsServiceImpl(this._serviceContext.invitationsManager),
268
267
 
@@ -294,6 +293,7 @@ export class ClientServicesHost {
294
293
  });
295
294
 
296
295
  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,35 +349,7 @@ 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.
354
352
  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 = await 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(Properties)!),
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();
380
-
381
353
  return identity;
382
354
  }
383
355
  }
@@ -3,10 +3,11 @@
3
3
  //
4
4
 
5
5
  import { Event } from '@dxos/async';
6
+ import { Resource, type Context } from '@dxos/context';
6
7
  import { type CredentialProcessor, type SpecificCredential, checkCredentialType } from '@dxos/credentials';
7
8
  import { type Credential, type Epoch } from '@dxos/protocols/proto/dxos/halo/credentials';
8
9
 
9
- export class AutomergeSpaceState implements CredentialProcessor {
10
+ export class AutomergeSpaceState extends Resource implements CredentialProcessor {
10
11
  public rootUrl: string | undefined = undefined;
11
12
  public lastEpoch: SpecificCredential<Epoch> | undefined = undefined;
12
13
 
@@ -14,7 +15,15 @@ export class AutomergeSpaceState implements CredentialProcessor {
14
15
 
15
16
  private _isProcessingRootDocs = false;
16
17
 
17
- constructor(private readonly _onNewRoot: (rootUrl: string) => void) {}
18
+ constructor(private readonly _onNewRoot: (rootUrl: string) => void) {
19
+ super();
20
+ }
21
+
22
+ protected override async _open(ctx: Context): Promise<void> {}
23
+
24
+ protected override async _close(ctx: Context): Promise<void> {
25
+ this._isProcessingRootDocs = false;
26
+ }
18
27
 
19
28
  async processCredential(credential: Credential) {
20
29
  if (!checkCredentialType(credential, 'dxos.halo.credentials.Epoch')) {
@@ -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 } from '../testing';
15
+ import { TestBuilder, type TestPeer } from '../testing';
16
16
 
17
17
  describe('DataSpaceManager', () => {
18
18
  test('create space', async () => {
@@ -168,5 +168,50 @@ 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
+ });
171
207
  });
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
+ };
172
217
  });
@@ -4,15 +4,16 @@
4
4
 
5
5
  import { Event, synchronized, trackLeaks } from '@dxos/async';
6
6
  import { type Doc } from '@dxos/automerge/automerge';
7
- import { type AutomergeUrl } from '@dxos/automerge/automerge-repo';
8
- import { cancelWithContext, Context } from '@dxos/context';
7
+ import { type AutomergeUrl, type DocHandle } from '@dxos/automerge/automerge-repo';
8
+ import { PropertiesType } from '@dxos/client-protocol';
9
+ import { Context, cancelWithContext } from '@dxos/context';
9
10
  import {
11
+ getCredentialAssertion,
10
12
  type CredentialSigner,
11
13
  type DelegateInvitationCredential,
12
- getCredentialAssertion,
13
14
  type MemberInfo,
14
15
  } from '@dxos/credentials';
15
- import { type EchoHost } from '@dxos/echo-db';
16
+ import { convertLegacyReferences, findInlineObjectOfType, type EchoHost } from '@dxos/echo-db';
16
17
  import {
17
18
  AuthStatus,
18
19
  type MetadataStore,
@@ -21,7 +22,14 @@ import {
21
22
  type SpaceProtocol,
22
23
  type SpaceProtocolSession,
23
24
  } from '@dxos/echo-pipeline';
24
- import { type SpaceDoc } from '@dxos/echo-protocol';
25
+ import {
26
+ LEGACY_TYPE_PROPERTIES,
27
+ SpaceDocVersion,
28
+ encodeReference,
29
+ type ObjectStructure,
30
+ type SpaceDoc,
31
+ } from '@dxos/echo-protocol';
32
+ import { TYPE_PROPERTIES, generateEchoId, getTypeReference } from '@dxos/echo-schema';
25
33
  import { type FeedStore } from '@dxos/feed-store';
26
34
  import { invariant } from '@dxos/invariant';
27
35
  import { type Keyring } from '@dxos/keyring';
@@ -31,15 +39,15 @@ import { trace as Trace } from '@dxos/protocols';
31
39
  import { Invitation, SpaceState } from '@dxos/protocols/proto/dxos/client/services';
32
40
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
33
41
  import { type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
34
- import { type Credential, type ProfileDocument, SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
42
+ import { SpaceMember, type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
35
43
  import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
36
44
  import { type PeerState } from '@dxos/protocols/proto/dxos/mesh/presence';
37
45
  import { Gossip, Presence } from '@dxos/teleport-extension-gossip';
38
46
  import { type Timeframe } from '@dxos/timeframe';
39
47
  import { trace } from '@dxos/tracing';
40
- import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
48
+ import { ComplexMap, assignDeep, deferFunction, forEachAsync } from '@dxos/util';
41
49
 
42
- import { DataSpace, findPropertiesObject } from './data-space';
50
+ import { DataSpace } from './data-space';
43
51
  import { spaceGenesis } from './genesis';
44
52
  import { createAuthProvider } from '../identity';
45
53
  import { type InvitationsManager } from '../invitations';
@@ -47,6 +55,9 @@ import { type InvitationsManager } from '../invitations';
47
55
  const PRESENCE_ANNOUNCE_INTERVAL = 10_000;
48
56
  const PRESENCE_OFFLINE_TIMEOUT = 20_000;
49
57
 
58
+ // Space properties key for default metadata.
59
+ const DEFAULT_SPACE_KEY = '__DEFAULT__';
60
+
50
61
  export interface SigningContext {
51
62
  identityKey: PublicKey;
52
63
  deviceKey: PublicKey;
@@ -88,8 +99,6 @@ export class DataSpaceManager {
88
99
 
89
100
  private _isOpen = false;
90
101
  private readonly _instanceId = PublicKey.random().toHex();
91
- private readonly _spaceMemberPresenceAnnounceInterval: number;
92
- private readonly _spaceMemberPresenceOfflineTimeout: number;
93
102
 
94
103
  constructor(
95
104
  private readonly _spaceManager: SpaceManager,
@@ -99,15 +108,8 @@ export class DataSpaceManager {
99
108
  private readonly _feedStore: FeedStore<FeedMessage>,
100
109
  private readonly _echoHost: EchoHost,
101
110
  private readonly _invitationsManager: InvitationsManager,
102
- params?: DataSpaceManagerRuntimeParams,
111
+ private readonly _params?: DataSpaceManagerRuntimeParams,
103
112
  ) {
104
- const {
105
- spaceMemberPresenceAnnounceInterval = PRESENCE_ANNOUNCE_INTERVAL,
106
- spaceMemberPresenceOfflineTimeout = PRESENCE_OFFLINE_TIMEOUT,
107
- } = params ?? {};
108
- this._spaceMemberPresenceAnnounceInterval = spaceMemberPresenceAnnounceInterval;
109
- this._spaceMemberPresenceOfflineTimeout = spaceMemberPresenceOfflineTimeout;
110
-
111
113
  trace.diagnostic({
112
114
  id: 'spaces',
113
115
  name: 'Spaces',
@@ -117,7 +119,7 @@ export class DataSpaceManager {
117
119
  const rootHandle = rootUrl ? this._echoHost.automergeRepo.find(rootUrl as AutomergeUrl) : undefined;
118
120
  const rootDoc = rootHandle?.docSync() as Doc<SpaceDoc> | undefined;
119
121
 
120
- const properties = rootDoc && findPropertiesObject(rootDoc);
122
+ const properties = rootDoc && findInlineObjectOfType(rootDoc, TYPE_PROPERTIES);
121
123
 
122
124
  return {
123
125
  key: space.key.toHex(),
@@ -157,12 +159,6 @@ export class DataSpaceManager {
157
159
  this._isOpen = true;
158
160
  this.updated.emit();
159
161
 
160
- for (const space of this._spaces.values()) {
161
- if (space.state !== SpaceState.INACTIVE) {
162
- space.initializeDataPipelineAsync();
163
- }
164
- }
165
-
166
162
  log.trace('dxos.echo.data-space-manager.open', Trace.end({ id: this._instanceId }));
167
163
  }
168
164
 
@@ -174,6 +170,7 @@ export class DataSpaceManager {
174
170
  for (const space of this._spaces.values()) {
175
171
  await space.close();
176
172
  }
173
+ this._spaces.clear();
177
174
  }
178
175
 
179
176
  /**
@@ -197,6 +194,7 @@ export class DataSpaceManager {
197
194
 
198
195
  const root = await this._echoHost.createSpaceRoot(spaceKey);
199
196
  const space = await this._constructSpace(metadata);
197
+ await space.open();
200
198
 
201
199
  const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner, root.url);
202
200
  await this._metadataStore.addSpace(metadata);
@@ -211,6 +209,61 @@ export class DataSpaceManager {
211
209
  return space;
212
210
  }
213
211
 
212
+ async isDefaultSpace(space: DataSpace): Promise<boolean> {
213
+ if (!space.databaseRoot) {
214
+ return false;
215
+ }
216
+ switch (space.databaseRoot.getVersion()) {
217
+ case SpaceDocVersion.CURRENT: {
218
+ const [_, properties] = findInlineObjectOfType(space.databaseRoot.docSync()!, TYPE_PROPERTIES) ?? [];
219
+ return properties?.data?.[DEFAULT_SPACE_KEY] === this._signingContext.identityKey.toHex();
220
+ }
221
+ case SpaceDocVersion.LEGACY: {
222
+ const convertedDoc = await convertLegacyReferences(space.databaseRoot.docSync()!);
223
+ const [_, properties] = findInlineObjectOfType(convertedDoc, LEGACY_TYPE_PROPERTIES) ?? [];
224
+ return properties?.data?.[DEFAULT_SPACE_KEY] === this._signingContext.identityKey.toHex();
225
+ }
226
+
227
+ default:
228
+ log.warn('unknown space version', { version: space.databaseRoot.getVersion(), spaceId: space.id });
229
+ return false;
230
+ }
231
+ }
232
+
233
+ async createDefaultSpace() {
234
+ const space = await this.createSpace();
235
+ const document = await this._getSpaceRootDocument(space);
236
+
237
+ // TODO(dmaretskyi): Better API for low-level data access.
238
+ const properties: ObjectStructure = {
239
+ system: {
240
+ type: encodeReference(getTypeReference(PropertiesType)!),
241
+ },
242
+ data: {
243
+ [DEFAULT_SPACE_KEY]: this._signingContext.identityKey.toHex(),
244
+ },
245
+ meta: {
246
+ keys: [],
247
+ },
248
+ };
249
+
250
+ const propertiesId = generateEchoId();
251
+ document.change((doc: SpaceDoc) => {
252
+ assignDeep(doc, ['objects', propertiesId], properties);
253
+ });
254
+
255
+ await this._echoHost.flush();
256
+ return space;
257
+ }
258
+
259
+ private async _getSpaceRootDocument(space: DataSpace): Promise<DocHandle<SpaceDoc>> {
260
+ const automergeIndex = space.automergeSpaceState.rootUrl;
261
+ invariant(automergeIndex);
262
+ const document = this._echoHost.automergeRepo.find<SpaceDoc>(automergeIndex as any);
263
+ await document.whenReady();
264
+ return document;
265
+ }
266
+
214
267
  // TODO(burdon): Rename join space.
215
268
  @synchronized
216
269
  async acceptSpace(opts: AcceptSpaceOptions): Promise<DataSpace> {
@@ -226,6 +279,7 @@ export class DataSpaceManager {
226
279
  };
227
280
 
228
281
  const space = await this._constructSpace(metadata);
282
+ await space.open();
229
283
  await this._metadataStore.addSpace(metadata);
230
284
  space.initializeDataPipelineAsync();
231
285
 
@@ -254,8 +308,8 @@ export class DataSpaceManager {
254
308
  localPeerId: this._signingContext.deviceKey,
255
309
  });
256
310
  const presence = new Presence({
257
- announceInterval: this._spaceMemberPresenceAnnounceInterval,
258
- offlineTimeout: this._spaceMemberPresenceOfflineTimeout,
311
+ announceInterval: this._params?.spaceMemberPresenceAnnounceInterval ?? PRESENCE_ANNOUNCE_INTERVAL,
312
+ offlineTimeout: this._params?.spaceMemberPresenceOfflineTimeout ?? PRESENCE_OFFLINE_TIMEOUT,
259
313
  identityKey: this._signingContext.identityKey,
260
314
  gossip,
261
315
  });
@@ -336,10 +390,6 @@ export class DataSpaceManager {
336
390
  }
337
391
  });
338
392
 
339
- if (metadata.state !== SpaceState.INACTIVE) {
340
- await dataSpace.open();
341
- }
342
-
343
393
  if (metadata.controlTimeframe) {
344
394
  dataSpace.inner.controlPipeline.state.setTargetTimeframe(metadata.controlTimeframe);
345
395
  }