@dxos/client-services 0.4.10-main.b3cf40d → 0.4.10-main.bb9f1bf

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 (113) hide show
  1. package/dist/lib/browser/{chunk-4ECD5R24.mjs → chunk-TM5QAJBN.mjs} +1439 -1073
  2. package/dist/lib/browser/chunk-TM5QAJBN.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +15 -3
  4. package/dist/lib/browser/index.mjs.map +1 -1
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/packlets/testing/index.mjs +136 -116
  7. package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/{chunk-OYV4Y6T4.cjs → chunk-ZMWT7BZY.cjs} +1357 -1079
  9. package/dist/lib/node/chunk-ZMWT7BZY.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +50 -38
  11. package/dist/lib/node/index.cjs.map +1 -1
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/packlets/testing/index.cjs +135 -118
  14. package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
  15. package/dist/types/src/index.d.ts +1 -0
  16. package/dist/types/src/index.d.ts.map +1 -1
  17. package/dist/types/src/packlets/diagnostics/browser-diagnostics-broadcast.d.ts +5 -0
  18. package/dist/types/src/packlets/diagnostics/browser-diagnostics-broadcast.d.ts.map +1 -0
  19. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts +5 -0
  20. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -0
  21. package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts +15 -0
  22. package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts.map +1 -0
  23. package/dist/types/src/packlets/{services → diagnostics}/diagnostics.d.ts +1 -1
  24. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -0
  25. package/dist/types/src/packlets/diagnostics/index.d.ts +4 -0
  26. package/dist/types/src/packlets/diagnostics/index.d.ts.map +1 -0
  27. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  28. package/dist/types/src/packlets/indexing/util.d.ts +2 -6
  29. package/dist/types/src/packlets/indexing/util.d.ts.map +1 -1
  30. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +3 -1
  31. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
  32. package/dist/types/src/packlets/invitations/index.d.ts +1 -0
  33. package/dist/types/src/packlets/invitations/index.d.ts.map +1 -1
  34. package/dist/types/src/packlets/invitations/invitation-extension.d.ts +1 -0
  35. package/dist/types/src/packlets/invitations/invitation-extension.d.ts.map +1 -1
  36. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +6 -1
  37. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
  38. package/dist/types/src/packlets/invitations/invitations-handler.d.ts +8 -4
  39. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  40. package/dist/types/src/packlets/invitations/invitations-manager.d.ts +44 -0
  41. package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -0
  42. package/dist/types/src/packlets/invitations/invitations-service.d.ts +7 -23
  43. package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
  44. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +2 -1
  45. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  46. package/dist/types/src/packlets/services/index.d.ts +1 -1
  47. package/dist/types/src/packlets/services/index.d.ts.map +1 -1
  48. package/dist/types/src/packlets/services/service-context.d.ts +9 -5
  49. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  50. package/dist/types/src/packlets/services/service-host.d.ts +6 -1
  51. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  52. package/dist/types/src/packlets/services/util.d.ts +1 -0
  53. package/dist/types/src/packlets/services/util.d.ts.map +1 -1
  54. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +5 -1
  55. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  56. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  57. package/dist/types/src/packlets/storage/index.d.ts +1 -0
  58. package/dist/types/src/packlets/storage/index.d.ts.map +1 -1
  59. package/dist/types/src/packlets/storage/level.d.ts +4 -0
  60. package/dist/types/src/packlets/storage/level.d.ts.map +1 -0
  61. package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
  62. package/dist/types/src/packlets/storage/util.d.ts +4 -0
  63. package/dist/types/src/packlets/storage/util.d.ts.map +1 -0
  64. package/dist/types/src/packlets/system/system-service.d.ts +1 -1
  65. package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
  66. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  67. package/dist/types/src/packlets/testing/test-builder.d.ts +7 -2
  68. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  69. package/dist/types/src/version.d.ts +1 -1
  70. package/package.json +36 -34
  71. package/src/index.ts +1 -0
  72. package/src/packlets/devices/devices-service.test.ts +1 -1
  73. package/src/packlets/diagnostics/browser-diagnostics-broadcast.ts +94 -0
  74. package/src/packlets/diagnostics/diagnostics-broadcast.ts +20 -0
  75. package/src/packlets/diagnostics/diagnostics-collector.ts +65 -0
  76. package/src/packlets/{services → diagnostics}/diagnostics.ts +2 -2
  77. package/src/packlets/diagnostics/index.ts +7 -0
  78. package/src/packlets/identity/identity-manager.ts +1 -0
  79. package/src/packlets/identity/identity-service.test.ts +1 -1
  80. package/src/packlets/identity/identity.test.ts +3 -0
  81. package/src/packlets/indexing/util.ts +9 -66
  82. package/src/packlets/invitations/device-invitation-protocol.test.ts +1 -1
  83. package/src/packlets/invitations/device-invitation-protocol.ts +6 -1
  84. package/src/packlets/invitations/index.ts +1 -0
  85. package/src/packlets/invitations/invitation-extension.ts +28 -1
  86. package/src/packlets/invitations/invitation-protocol.ts +7 -1
  87. package/src/packlets/invitations/invitations-handler.ts +75 -96
  88. package/src/packlets/invitations/invitations-manager.ts +271 -0
  89. package/src/packlets/invitations/invitations-service.ts +23 -168
  90. package/src/packlets/invitations/space-invitation-protocol.ts +45 -3
  91. package/src/packlets/network/network-service.test.ts +1 -1
  92. package/src/packlets/services/automerge-host.test.ts +10 -4
  93. package/src/packlets/services/index.ts +1 -1
  94. package/src/packlets/services/service-context.test.ts +9 -6
  95. package/src/packlets/services/service-context.ts +29 -10
  96. package/src/packlets/services/service-host.ts +61 -29
  97. package/src/packlets/services/service-registry.test.ts +1 -1
  98. package/src/packlets/services/util.ts +2 -0
  99. package/src/packlets/spaces/data-space-manager.test.ts +4 -4
  100. package/src/packlets/spaces/data-space-manager.ts +48 -2
  101. package/src/packlets/spaces/data-space.ts +1 -1
  102. package/src/packlets/spaces/spaces-service.test.ts +1 -1
  103. package/src/packlets/storage/index.ts +1 -0
  104. package/src/packlets/storage/level.ts +19 -0
  105. package/src/packlets/storage/storage.ts +3 -9
  106. package/src/packlets/storage/util.ts +19 -0
  107. package/src/packlets/system/system-service.ts +1 -1
  108. package/src/packlets/testing/invitation-utils.ts +100 -97
  109. package/src/packlets/testing/test-builder.ts +42 -6
  110. package/src/version.ts +1 -1
  111. package/dist/lib/browser/chunk-4ECD5R24.mjs.map +0 -7
  112. package/dist/lib/node/chunk-OYV4Y6T4.cjs.map +0 -7
  113. package/dist/types/src/packlets/services/diagnostics.d.ts.map +0 -1
@@ -2,14 +2,21 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
- import { Event, sleep, synchronized } from '@dxos/async';
6
- import { clientServiceBundle, defaultKey, type ClientServices, PropertiesSchema } from '@dxos/client-protocol';
5
+ import { type Level } from 'level';
6
+
7
+ import { Event, synchronized } from '@dxos/async';
8
+ import { clientServiceBundle, defaultKey, type ClientServices, Properties } from '@dxos/client-protocol';
7
9
  import { type Config } from '@dxos/config';
8
10
  import { Context } from '@dxos/context';
9
- import { DataServiceImpl, type SpaceDoc } from '@dxos/echo-pipeline';
10
- import * as E from '@dxos/echo-schema';
11
- import { createRawObjectDoc } from '@dxos/echo-schema';
12
- import { IndexServiceImpl } from '@dxos/indexing';
11
+ import {
12
+ DataServiceImpl,
13
+ type ObjectStructure,
14
+ encodeReference,
15
+ type SpaceDoc,
16
+ type LevelDB,
17
+ } from '@dxos/echo-pipeline';
18
+ import { getTypeReference } from '@dxos/echo-schema';
19
+ import { QueryServiceImpl } from '@dxos/indexing';
13
20
  import { invariant } from '@dxos/invariant';
14
21
  import { PublicKey } from '@dxos/keys';
15
22
  import { log } from '@dxos/log';
@@ -22,18 +29,22 @@ import { TRACE_PROCESSOR, trace as Trace } from '@dxos/tracing';
22
29
  import { assignDeep } from '@dxos/util';
23
30
  import { WebsocketRpcClient } from '@dxos/websocket-rpc';
24
31
 
25
- import { createDiagnostics } from './diagnostics';
26
32
  import { ServiceContext, type ServiceContextRuntimeParams } from './service-context';
27
33
  import { ServiceRegistry } from './service-registry';
28
34
  import { DevicesServiceImpl } from '../devices';
29
35
  import { DevtoolsHostEvents, DevtoolsServiceImpl } from '../devtools';
36
+ import {
37
+ type CollectDiagnosticsBroadcastHandler,
38
+ createCollectDiagnosticsBroadcastHandler,
39
+ createDiagnostics,
40
+ } from '../diagnostics';
30
41
  import { IdentityServiceImpl, type CreateIdentityOptions } from '../identity';
31
42
  import { InvitationsServiceImpl } from '../invitations';
32
43
  import { Lock, type ResourceLock } from '../locks';
33
44
  import { LoggingServiceImpl } from '../logging';
34
45
  import { NetworkServiceImpl } from '../network';
35
46
  import { SpacesServiceImpl } from '../spaces';
36
- import { createStorageObjects } from '../storage';
47
+ import { createLevel, createStorageObjects } from '../storage';
37
48
  import { SystemServiceImpl } from '../system';
38
49
 
39
50
  export type ClientServicesHostParams = {
@@ -45,6 +56,7 @@ export type ClientServicesHostParams = {
45
56
  signalManager?: SignalManager;
46
57
  connectionLog?: boolean;
47
58
  storage?: Storage;
59
+ level?: LevelDB;
48
60
  lockKey?: string;
49
61
  callbacks?: ClientServicesHostCallbacks;
50
62
  runtimeParams?: ServiceContextRuntimeParams;
@@ -71,17 +83,20 @@ export class ClientServicesHost {
71
83
  private readonly _systemService: SystemServiceImpl;
72
84
  private readonly _loggingService: LoggingServiceImpl;
73
85
  private readonly _tracingService = TRACE_PROCESSOR.createTraceSender();
86
+ private _queryService!: QueryServiceImpl;
74
87
 
75
88
  private _config?: Config;
76
89
  private readonly _statusUpdate = new Event<void>();
77
90
  private _signalManager?: SignalManager;
78
91
  private _networkManager?: NetworkManager;
79
92
  private _storage?: Storage;
93
+ private _level?: Level<string, string>;
80
94
  private _callbacks?: ClientServicesHostCallbacks;
81
95
  private _devtoolsProxy?: WebsocketRpcClient<{}, ClientServices>;
82
96
 
83
97
  private _serviceContext!: ServiceContext;
84
98
  private readonly _runtimeParams?: ServiceContextRuntimeParams;
99
+ private diagnosticsBroadcastHandler: CollectDiagnosticsBroadcastHandler;
85
100
 
86
101
  @Trace.info()
87
102
  private _opening = false;
@@ -94,12 +109,14 @@ export class ClientServicesHost {
94
109
  transportFactory,
95
110
  signalManager,
96
111
  storage,
112
+ level,
97
113
  // TODO(wittjosiah): Turn this on by default.
98
114
  lockKey,
99
115
  callbacks,
100
116
  runtimeParams,
101
117
  }: ClientServicesHostParams = {}) {
102
118
  this._storage = storage;
119
+ this._level = level;
103
120
  this._callbacks = callbacks;
104
121
  this._runtimeParams = runtimeParams;
105
122
 
@@ -139,6 +156,7 @@ export class ClientServicesHost {
139
156
  },
140
157
  });
141
158
 
159
+ this.diagnosticsBroadcastHandler = createCollectDiagnosticsBroadcastHandler(this._systemService);
142
160
  this._loggingService = new LoggingServiceImpl();
143
161
 
144
162
  this._serviceRegistry = new ServiceRegistry<ClientServices>(clientServiceBundle, {
@@ -227,17 +245,30 @@ export class ClientServicesHost {
227
245
 
228
246
  this._opening = true;
229
247
  log('opening...', { lockKey: this._resourceLock?.lockKey });
248
+
249
+ if (!this._level) {
250
+ this._level = await createLevel(this._config.get('runtime.client.storage', {})!);
251
+ }
252
+ await this._level.open();
253
+
230
254
  await this._resourceLock?.acquire();
231
255
 
232
256
  await this._loggingService.open();
233
257
 
234
258
  this._serviceContext = new ServiceContext(
235
259
  this._storage,
260
+ this._level,
236
261
  this._networkManager,
237
262
  this._signalManager,
238
263
  this._runtimeParams,
239
264
  );
240
265
 
266
+ this._queryService = new QueryServiceImpl({
267
+ indexer: this._serviceContext.indexer,
268
+ automergeHost: this._serviceContext.automergeHost,
269
+ });
270
+ await this._queryService.open(ctx);
271
+
241
272
  this._serviceRegistry.setServices({
242
273
  SystemService: this._systemService,
243
274
 
@@ -248,11 +279,7 @@ export class ClientServicesHost {
248
279
  (profile) => this._serviceContext.broadcastProfileUpdate(profile),
249
280
  ),
250
281
 
251
- InvitationsService: new InvitationsServiceImpl(
252
- this._serviceContext.invitations,
253
- (invitation) => this._serviceContext.getInvitationHandler(invitation),
254
- this._serviceContext.metadataStore,
255
- ),
282
+ InvitationsService: new InvitationsServiceImpl(this._serviceContext.invitationsManager),
256
283
 
257
284
  DevicesService: new DevicesServiceImpl(this._serviceContext.identityManager),
258
285
 
@@ -267,10 +294,7 @@ export class ClientServicesHost {
267
294
 
268
295
  DataService: new DataServiceImpl(this._serviceContext.automergeHost),
269
296
 
270
- IndexService: new IndexServiceImpl({
271
- indexer: this._serviceContext.indexer,
272
- automergeHost: this._serviceContext.automergeHost,
273
- }),
297
+ QueryService: this._queryService,
274
298
 
275
299
  NetworkService: new NetworkServiceImpl(this._serviceContext.networkManager, this._serviceContext.signalManager),
276
300
 
@@ -286,11 +310,6 @@ export class ClientServicesHost {
286
310
  });
287
311
 
288
312
  await this._serviceContext.open(ctx);
289
- // TODO(nf): move to InvitationManager in ServiceContext?
290
- invariant(this.serviceRegistry.services.InvitationsService);
291
- const loadedInvitations = await this.serviceRegistry.services.InvitationsService.loadPersistentInvitations();
292
-
293
- log('loaded persistent invitations', { count: loadedInvitations.invitations?.length });
294
313
 
295
314
  const devtoolsProxy = this._config?.get('runtime.client.devtoolsProxy');
296
315
  if (devtoolsProxy) {
@@ -302,6 +321,7 @@ export class ClientServicesHost {
302
321
  });
303
322
  void this._devtoolsProxy.open();
304
323
  }
324
+ this.diagnosticsBroadcastHandler.start();
305
325
 
306
326
  this._opening = false;
307
327
  this._open = true;
@@ -320,10 +340,13 @@ export class ClientServicesHost {
320
340
 
321
341
  const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
322
342
  log('closing...', { deviceKey });
343
+ this.diagnosticsBroadcastHandler.stop();
323
344
  await this._devtoolsProxy?.close();
324
345
  this._serviceRegistry.setServices({ SystemService: this._systemService });
325
346
  await this._loggingService.close();
347
+ await this._queryService.close();
326
348
  await this._serviceContext.close();
349
+ await this._level?.close();
327
350
  this._open = false;
328
351
  this._statusUpdate.emit();
329
352
  log('closed', { deviceKey });
@@ -353,15 +376,24 @@ export class ClientServicesHost {
353
376
  const document = await this._serviceContext.automergeHost.repo.find<SpaceDoc>(automergeIndex as any);
354
377
  await document.whenReady();
355
378
 
356
- const objectDocument = createRawObjectDoc(
357
- { [defaultKey]: identity.identityKey.toHex() },
358
- { type: E.getTypeReference(PropertiesSchema) },
359
- );
379
+ // TODO(dmaretskyi): Better API for low-level data access.
380
+ const properties: ObjectStructure = {
381
+ system: {
382
+ type: encodeReference(getTypeReference(Properties)!),
383
+ },
384
+ data: {
385
+ [defaultKey]: identity.identityKey.toHex(),
386
+ },
387
+ meta: {
388
+ keys: [],
389
+ },
390
+ };
391
+ const propertiesId = PublicKey.random().toHex();
360
392
  document.change((doc: SpaceDoc) => {
361
- assignDeep(doc, ['objects', objectDocument.id], objectDocument.handle.docSync());
393
+ assignDeep(doc, ['objects', propertiesId], properties);
362
394
  });
363
- // TODO: replace with flush when supported by automerge-repo
364
- await sleep(200);
395
+
396
+ await this._serviceContext.automergeHost.repo.flush();
365
397
 
366
398
  return identity;
367
399
  }
@@ -31,7 +31,7 @@ const serviceBundle = createServiceBundle<TestServices>({
31
31
  describe('service registry', () => {
32
32
  test('builds a service registry', async () => {
33
33
  const remoteSource = 'https://remote.source';
34
- const serviceContext = createServiceContext();
34
+ const serviceContext = await createServiceContext();
35
35
  await serviceContext.open(new Context());
36
36
 
37
37
  const serviceRegistry = new ServiceRegistry(serviceBundle, {
@@ -5,6 +5,8 @@
5
5
  import { PublicKey } from '@dxos/keys';
6
6
  import { humanize } from '@dxos/util';
7
7
 
8
+ export const ClientServicesProviderResource = Symbol.for('dxos.resource.ClientServices');
9
+
8
10
  // TODO(burdon): Move to util.
9
11
 
10
12
  export type JsonStringifyOptions = {
@@ -20,7 +20,7 @@ describe('DataSpaceManager', () => {
20
20
 
21
21
  const peer = builder.createPeer();
22
22
  await peer.createIdentity();
23
- await openAndClose(peer.dataSpaceManager);
23
+ await openAndClose(peer.automergeHost, peer.dataSpaceManager);
24
24
 
25
25
  const space = await peer.dataSpaceManager.createSpace();
26
26
 
@@ -42,7 +42,7 @@ describe('DataSpaceManager', () => {
42
42
  const peer2 = builder.createPeer();
43
43
  await peer2.createIdentity();
44
44
 
45
- await openAndClose(peer1.dataSpaceManager, peer2.dataSpaceManager);
45
+ await openAndClose(peer1.automergeHost, peer1.dataSpaceManager, peer2.automergeHost, peer2.dataSpaceManager);
46
46
 
47
47
  const space1 = await peer1.dataSpaceManager.createSpace();
48
48
  await space1.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
@@ -111,7 +111,7 @@ describe('DataSpaceManager', () => {
111
111
  await peer2.createIdentity();
112
112
  await peer2.dataSpaceManager.open();
113
113
 
114
- await openAndClose(peer1.dataSpaceManager, peer2.dataSpaceManager);
114
+ await openAndClose(peer1.automergeHost, peer1.dataSpaceManager, peer2.automergeHost, peer2.dataSpaceManager);
115
115
 
116
116
  const space1 = await peer1.dataSpaceManager.createSpace();
117
117
  await space1.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
@@ -153,7 +153,7 @@ describe('DataSpaceManager', () => {
153
153
 
154
154
  const peer = builder.createPeer();
155
155
  await peer.createIdentity();
156
- await openAndClose(peer.dataSpaceManager);
156
+ await openAndClose(peer.automergeHost, peer.dataSpaceManager);
157
157
 
158
158
  const space = await peer.dataSpaceManager.createSpace();
159
159
  await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { Event, synchronized, trackLeaks } from '@dxos/async';
6
6
  import { Context, cancelWithContext } from '@dxos/context';
7
- import { getCredentialAssertion, type CredentialSigner } from '@dxos/credentials';
7
+ import { getCredentialAssertion, type CredentialSigner, type DelegateInvitationCredential } from '@dxos/credentials';
8
8
  import { type AutomergeHost, type MetadataStore, type Space, type SpaceManager } from '@dxos/echo-pipeline';
9
9
  import { type FeedStore } from '@dxos/feed-store';
10
10
  import { invariant } from '@dxos/invariant';
@@ -12,10 +12,11 @@ import { type Keyring } from '@dxos/keyring';
12
12
  import { PublicKey } from '@dxos/keys';
13
13
  import { log } from '@dxos/log';
14
14
  import { trace } from '@dxos/protocols';
15
- import { SpaceState } from '@dxos/protocols/proto/dxos/client/services';
15
+ import { Invitation, SpaceState } from '@dxos/protocols/proto/dxos/client/services';
16
16
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
17
17
  import { type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
18
18
  import { type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
19
+ import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
19
20
  import { Gossip, Presence } from '@dxos/teleport-extension-gossip';
20
21
  import { type Timeframe } from '@dxos/timeframe';
21
22
  import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
@@ -23,6 +24,7 @@ import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
23
24
  import { DataSpace } from './data-space';
24
25
  import { spaceGenesis } from './genesis';
25
26
  import { createAuthProvider } from '../identity';
27
+ import { type InvitationsManager } from '../invitations';
26
28
 
27
29
  const PRESENCE_ANNOUNCE_INTERVAL = 10_000;
28
30
  const PRESENCE_OFFLINE_TIMEOUT = 20_000;
@@ -78,6 +80,7 @@ export class DataSpaceManager {
78
80
  private readonly _signingContext: SigningContext,
79
81
  private readonly _feedStore: FeedStore<FeedMessage>,
80
82
  private readonly _automergeHost: AutomergeHost,
83
+ private readonly _invitationsManager: InvitationsManager,
81
84
  params?: DataSpaceManagerRuntimeParams,
82
85
  ) {
83
86
  const {
@@ -247,6 +250,9 @@ export class DataSpaceManager {
247
250
  log.warn('auth failure');
248
251
  },
249
252
  memberKey: this._signingContext.identityKey,
253
+ onDelegatedInvitationStatusChange: (invitation, isActive) => {
254
+ return this._handleInvitationStatusChange(dataSpace, invitation, isActive);
255
+ },
250
256
  });
251
257
  controlFeed && (await space.setControlFeed(controlFeed));
252
258
  dataFeed && (await space.setDataFeed(dataFeed));
@@ -267,6 +273,7 @@ export class DataSpaceManager {
267
273
  afterReady: async () => {
268
274
  log('after space ready', { space: space.key, open: this._isOpen });
269
275
  if (this._isOpen) {
276
+ await this._createDelegatedInvitations(dataSpace, [...space.spaceState.invitations.entries()]);
270
277
  this.updated.emit();
271
278
  }
272
279
  },
@@ -289,4 +296,43 @@ export class DataSpaceManager {
289
296
  this._spaces.set(metadata.key, dataSpace);
290
297
  return dataSpace;
291
298
  }
299
+
300
+ private async _handleInvitationStatusChange(
301
+ dataSpace: DataSpace | undefined,
302
+ delegatedInvitation: DelegateInvitationCredential,
303
+ isActive: boolean,
304
+ ): Promise<void> {
305
+ if (dataSpace?.state !== SpaceState.READY) {
306
+ return;
307
+ }
308
+ if (isActive) {
309
+ await this._createDelegatedInvitations(dataSpace, [
310
+ [delegatedInvitation.credentialId, delegatedInvitation.invitation],
311
+ ]);
312
+ } else {
313
+ await this._invitationsManager.cancelInvitation(delegatedInvitation.invitation);
314
+ }
315
+ }
316
+
317
+ private async _createDelegatedInvitations(
318
+ space: DataSpace,
319
+ invitations: Array<[PublicKey, DelegateSpaceInvitation]>,
320
+ ): Promise<void> {
321
+ const tasks = invitations.map(([credentialId, invitation]) => {
322
+ return this._invitationsManager.createInvitation({
323
+ type: Invitation.Type.DELEGATED,
324
+ kind: Invitation.Kind.SPACE,
325
+ spaceKey: space.key,
326
+ authMethod: invitation.authMethod,
327
+ invitationId: invitation.invitationId,
328
+ swarmKey: invitation.swarmKey,
329
+ guestKeypair: invitation.guestKey ? { publicKey: invitation.guestKey } : undefined,
330
+ lifetime: invitation.expiresOn ? invitation.expiresOn.getTime() - Date.now() : undefined,
331
+ multiUse: invitation.multiUse,
332
+ delegationCredentialId: credentialId,
333
+ persistent: false,
334
+ });
335
+ });
336
+ await Promise.all(tasks);
337
+ }
292
338
  }
@@ -6,7 +6,6 @@ import { Event, asyncTimeout, scheduleTask, sleep, synchronized, trackLeaks } fr
6
6
  import { AUTH_TIMEOUT } from '@dxos/client-protocol';
7
7
  import { cancelWithContext, Context, ContextDisposedError } from '@dxos/context';
8
8
  import { timed, warnAfterTimeout } from '@dxos/debug';
9
- import { TYPE_PROPERTIES } from '@dxos/echo-db';
10
9
  import {
11
10
  type MetadataStore,
12
11
  type Space,
@@ -15,6 +14,7 @@ import {
15
14
  type SpaceDoc,
16
15
  } from '@dxos/echo-pipeline';
17
16
  import { AutomergeDocumentLoaderImpl } from '@dxos/echo-pipeline';
17
+ import { TYPE_PROPERTIES } from '@dxos/echo-schema';
18
18
  import { type FeedStore } from '@dxos/feed-store';
19
19
  import { failedInvariant, invariant } from '@dxos/invariant';
20
20
  import { type Keyring } from '@dxos/keyring';
@@ -22,7 +22,7 @@ describe('SpacesService', () => {
22
22
  let spacesService: SpacesService;
23
23
 
24
24
  beforeEach(async () => {
25
- serviceContext = createServiceContext();
25
+ serviceContext = await createServiceContext();
26
26
  await serviceContext.open(new Context());
27
27
  spacesService = new SpacesServiceImpl(serviceContext.identityManager, serviceContext.spaceManager, async () => {
28
28
  await serviceContext.initialized.wait();
@@ -3,3 +3,4 @@
3
3
  //
4
4
 
5
5
  export * from './storage';
6
+ export * from './level';
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { Level } from 'level';
6
+ import path from 'node:path';
7
+
8
+ import { PublicKey } from '@dxos/keys';
9
+ import { type Runtime } from '@dxos/protocols/proto/dxos/config';
10
+
11
+ import { getRootPath, isPersistent } from './util';
12
+
13
+ export const createLevel = async (config: Runtime.Client.Storage) => {
14
+ const persistent = isPersistent(config);
15
+ const storagePath = persistent ? path.join(getRootPath(config), 'level') : `/tmp/dxos-${PublicKey.random().toHex()}`;
16
+ const level = new Level<string, string>(storagePath);
17
+ await level.open();
18
+ return level;
19
+ };
@@ -4,22 +4,16 @@
4
4
  // Copyright 2023 DXOS.org
5
5
  //
6
6
 
7
- import { DX_DATA } from '@dxos/client-protocol';
8
7
  import { InvalidConfigError } from '@dxos/protocols';
9
8
  import { Runtime } from '@dxos/protocols/proto/dxos/config';
10
9
  import { createStorage, StorageType } from '@dxos/random-access-storage';
11
- import { isNode } from '@dxos/util';
12
10
 
13
11
  import StorageDriver = Runtime.Client.Storage.StorageDriver;
12
+ import { getRootPath } from './util';
14
13
 
15
14
  // TODO(burdon): Factor out.
16
15
  export const createStorageObjects = (config: Runtime.Client.Storage) => {
17
- const {
18
- persistent = false,
19
- keyStore,
20
- dataStore,
21
- dataRoot = isNode() ? DX_DATA : 'dxos/storage', // TODO(burdon): Factor out const.
22
- } = config ?? {};
16
+ const { persistent = false, keyStore, dataStore } = config ?? {};
23
17
 
24
18
  if (persistent && dataStore === StorageDriver.RAM) {
25
19
  throw new InvalidConfigError('RAM storage cannot be used in persistent mode.');
@@ -37,7 +31,7 @@ export const createStorageObjects = (config: Runtime.Client.Storage) => {
37
31
  return {
38
32
  storage: createStorage({
39
33
  type: persistent ? toStorageType(dataStore) : StorageType.RAM,
40
- root: `${dataRoot}/`,
34
+ root: getRootPath(config),
41
35
  }),
42
36
  };
43
37
  };
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { DX_DATA } from '@dxos/client-protocol';
6
+ import { Runtime } from '@dxos/protocols/proto/dxos/config';
7
+ import { isNode } from '@dxos/util';
8
+
9
+ export const getRootPath = (config: Runtime.Client.Storage) => {
10
+ const { dataRoot = isNode() ? DX_DATA : 'dxos/storage' } = config ?? {};
11
+ return `${dataRoot}/`;
12
+ };
13
+
14
+ export const isPersistent = (config: Runtime.Client.Storage) => {
15
+ const { persistent = false } = config ?? {};
16
+ return (
17
+ (config.dataStore !== undefined && config.dataStore !== Runtime.Client.Storage.StorageDriver.RAM) || persistent
18
+ );
19
+ };
@@ -16,7 +16,7 @@ import {
16
16
  } from '@dxos/protocols/proto/dxos/client/services';
17
17
  import { jsonKeyReplacer, type MaybePromise } from '@dxos/util';
18
18
 
19
- import { type Diagnostics } from '../services';
19
+ import { type Diagnostics } from '../diagnostics';
20
20
  import { getPlatform } from '../services/platform';
21
21
 
22
22
  export type SystemServiceOptions = {