@dxos/client-services 0.4.10-main.3e0c8a5 → 0.4.10-main.3f5e2d2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/{chunk-6GHZYBUW.mjs → chunk-HIQTBJPW.mjs} +1452 -1145
- package/dist/lib/browser/chunk-HIQTBJPW.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +20 -6
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +141 -119
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-D5VK6MZQ.cjs → chunk-JGUWA36I.cjs} +1349 -1126
- package/dist/lib/node/chunk-JGUWA36I.cjs.map +7 -0
- package/dist/lib/node/index.cjs +55 -41
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +139 -120
- package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/browser-diagnostics-broadcast.d.ts +5 -0
- package/dist/types/src/packlets/diagnostics/browser-diagnostics-broadcast.d.ts.map +1 -0
- package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts +5 -0
- package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -0
- package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts +15 -0
- package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts.map +1 -0
- package/dist/types/src/packlets/{services → diagnostics}/diagnostics.d.ts +1 -1
- package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -0
- package/dist/types/src/packlets/diagnostics/index.d.ts +4 -0
- package/dist/types/src/packlets/diagnostics/index.d.ts.map +1 -0
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +3 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/index.d.ts +1 -0
- package/dist/types/src/packlets/invitations/index.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-extension.d.ts +1 -0
- package/dist/types/src/packlets/invitations/invitation-extension.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +6 -1
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +8 -4
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts +44 -0
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/invitations-service.d.ts +7 -23
- package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +2 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/services/index.d.ts +1 -1
- package/dist/types/src/packlets/services/index.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +12 -10
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +5 -1
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/services/util.d.ts +1 -0
- package/dist/types/src/packlets/services/util.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +8 -3
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +4 -3
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/index.d.ts +1 -0
- package/dist/types/src/packlets/storage/index.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/level.d.ts +4 -0
- package/dist/types/src/packlets/storage/level.d.ts.map +1 -0
- package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/util.d.ts +4 -0
- package/dist/types/src/packlets/storage/util.d.ts.map +1 -0
- package/dist/types/src/packlets/system/system-service.d.ts +1 -1
- package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +10 -4
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/package.json +36 -34
- package/src/index.ts +1 -0
- package/src/packlets/devices/devices-service.test.ts +1 -1
- package/src/packlets/diagnostics/browser-diagnostics-broadcast.ts +94 -0
- package/src/packlets/diagnostics/diagnostics-broadcast.ts +20 -0
- package/src/packlets/diagnostics/diagnostics-collector.ts +65 -0
- package/src/packlets/{services → diagnostics}/diagnostics.ts +2 -2
- package/src/packlets/diagnostics/index.ts +7 -0
- package/src/packlets/identity/identity-manager.ts +1 -0
- package/src/packlets/identity/identity-service.test.ts +1 -1
- package/src/packlets/identity/identity.test.ts +3 -0
- package/src/packlets/invitations/device-invitation-protocol.test.ts +1 -1
- package/src/packlets/invitations/device-invitation-protocol.ts +6 -1
- package/src/packlets/invitations/index.ts +1 -0
- package/src/packlets/invitations/invitation-extension.ts +28 -1
- package/src/packlets/invitations/invitation-protocol.ts +7 -1
- package/src/packlets/invitations/invitations-handler.ts +75 -96
- package/src/packlets/invitations/invitations-manager.ts +271 -0
- package/src/packlets/invitations/invitations-service.ts +23 -168
- package/src/packlets/invitations/space-invitation-protocol.ts +45 -3
- package/src/packlets/network/network-service.test.ts +1 -1
- package/src/packlets/services/automerge-host.test.ts +13 -7
- package/src/packlets/services/index.ts +1 -1
- package/src/packlets/services/service-context.test.ts +9 -6
- package/src/packlets/services/service-context.ts +31 -26
- package/src/packlets/services/service-host.test.ts +6 -0
- package/src/packlets/services/service-host.ts +48 -32
- package/src/packlets/services/service-registry.test.ts +1 -1
- package/src/packlets/services/util.ts +2 -0
- package/src/packlets/spaces/data-space-manager.test.ts +4 -4
- package/src/packlets/spaces/data-space-manager.ts +56 -13
- package/src/packlets/spaces/data-space.ts +14 -19
- package/src/packlets/spaces/spaces-service.test.ts +1 -1
- package/src/packlets/storage/index.ts +1 -0
- package/src/packlets/storage/level.ts +19 -0
- package/src/packlets/storage/storage.ts +3 -9
- package/src/packlets/storage/util.ts +19 -0
- package/src/packlets/system/system-service.ts +1 -1
- package/src/packlets/testing/invitation-utils.ts +100 -97
- package/src/packlets/testing/test-builder.ts +40 -9
- package/src/packlets/vault/worker-runtime.ts +3 -1
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-6GHZYBUW.mjs.map +0 -7
- package/dist/lib/node/chunk-D5VK6MZQ.cjs.map +0 -7
- package/dist/types/src/packlets/indexing/index.d.ts +0 -2
- package/dist/types/src/packlets/indexing/index.d.ts.map +0 -1
- package/dist/types/src/packlets/indexing/util.d.ts +0 -15
- package/dist/types/src/packlets/indexing/util.d.ts.map +0 -1
- package/dist/types/src/packlets/services/diagnostics.d.ts.map +0 -1
- package/src/packlets/indexing/index.ts +0 -5
- package/src/packlets/indexing/util.ts +0 -89
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
// Copyright 2021 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
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 {
|
|
10
|
-
import
|
|
11
|
-
import { createRawObjectDoc } from '@dxos/echo-schema';
|
|
12
|
-
import { IndexServiceImpl } from '@dxos/indexing';
|
|
11
|
+
import { type ObjectStructure, encodeReference, type SpaceDoc, type LevelDB } from '@dxos/echo-pipeline';
|
|
12
|
+
import { getTypeReference } from '@dxos/echo-schema';
|
|
13
13
|
import { invariant } from '@dxos/invariant';
|
|
14
14
|
import { PublicKey } from '@dxos/keys';
|
|
15
15
|
import { log } from '@dxos/log';
|
|
@@ -22,18 +22,22 @@ import { TRACE_PROCESSOR, trace as Trace } from '@dxos/tracing';
|
|
|
22
22
|
import { assignDeep } from '@dxos/util';
|
|
23
23
|
import { WebsocketRpcClient } from '@dxos/websocket-rpc';
|
|
24
24
|
|
|
25
|
-
import { createDiagnostics } from './diagnostics';
|
|
26
25
|
import { ServiceContext, type ServiceContextRuntimeParams } from './service-context';
|
|
27
26
|
import { ServiceRegistry } from './service-registry';
|
|
28
27
|
import { DevicesServiceImpl } from '../devices';
|
|
29
28
|
import { DevtoolsHostEvents, DevtoolsServiceImpl } from '../devtools';
|
|
29
|
+
import {
|
|
30
|
+
type CollectDiagnosticsBroadcastHandler,
|
|
31
|
+
createCollectDiagnosticsBroadcastHandler,
|
|
32
|
+
createDiagnostics,
|
|
33
|
+
} from '../diagnostics';
|
|
30
34
|
import { IdentityServiceImpl, type CreateIdentityOptions } from '../identity';
|
|
31
35
|
import { InvitationsServiceImpl } from '../invitations';
|
|
32
36
|
import { Lock, type ResourceLock } from '../locks';
|
|
33
37
|
import { LoggingServiceImpl } from '../logging';
|
|
34
38
|
import { NetworkServiceImpl } from '../network';
|
|
35
39
|
import { SpacesServiceImpl } from '../spaces';
|
|
36
|
-
import { createStorageObjects } from '../storage';
|
|
40
|
+
import { createLevel, createStorageObjects } from '../storage';
|
|
37
41
|
import { SystemServiceImpl } from '../system';
|
|
38
42
|
|
|
39
43
|
export type ClientServicesHostParams = {
|
|
@@ -45,6 +49,7 @@ export type ClientServicesHostParams = {
|
|
|
45
49
|
signalManager?: SignalManager;
|
|
46
50
|
connectionLog?: boolean;
|
|
47
51
|
storage?: Storage;
|
|
52
|
+
level?: LevelDB;
|
|
48
53
|
lockKey?: string;
|
|
49
54
|
callbacks?: ClientServicesHostCallbacks;
|
|
50
55
|
runtimeParams?: ServiceContextRuntimeParams;
|
|
@@ -77,11 +82,13 @@ export class ClientServicesHost {
|
|
|
77
82
|
private _signalManager?: SignalManager;
|
|
78
83
|
private _networkManager?: NetworkManager;
|
|
79
84
|
private _storage?: Storage;
|
|
85
|
+
private _level?: Level<string, string>;
|
|
80
86
|
private _callbacks?: ClientServicesHostCallbacks;
|
|
81
87
|
private _devtoolsProxy?: WebsocketRpcClient<{}, ClientServices>;
|
|
82
88
|
|
|
83
89
|
private _serviceContext!: ServiceContext;
|
|
84
90
|
private readonly _runtimeParams?: ServiceContextRuntimeParams;
|
|
91
|
+
private diagnosticsBroadcastHandler: CollectDiagnosticsBroadcastHandler;
|
|
85
92
|
|
|
86
93
|
@Trace.info()
|
|
87
94
|
private _opening = false;
|
|
@@ -94,12 +101,14 @@ export class ClientServicesHost {
|
|
|
94
101
|
transportFactory,
|
|
95
102
|
signalManager,
|
|
96
103
|
storage,
|
|
104
|
+
level,
|
|
97
105
|
// TODO(wittjosiah): Turn this on by default.
|
|
98
106
|
lockKey,
|
|
99
107
|
callbacks,
|
|
100
108
|
runtimeParams,
|
|
101
109
|
}: ClientServicesHostParams = {}) {
|
|
102
110
|
this._storage = storage;
|
|
111
|
+
this._level = level;
|
|
103
112
|
this._callbacks = callbacks;
|
|
104
113
|
this._runtimeParams = runtimeParams;
|
|
105
114
|
|
|
@@ -139,6 +148,7 @@ export class ClientServicesHost {
|
|
|
139
148
|
},
|
|
140
149
|
});
|
|
141
150
|
|
|
151
|
+
this.diagnosticsBroadcastHandler = createCollectDiagnosticsBroadcastHandler(this._systemService);
|
|
142
152
|
this._loggingService = new LoggingServiceImpl();
|
|
143
153
|
|
|
144
154
|
this._serviceRegistry = new ServiceRegistry<ClientServices>(clientServiceBundle, {
|
|
@@ -227,12 +237,19 @@ export class ClientServicesHost {
|
|
|
227
237
|
|
|
228
238
|
this._opening = true;
|
|
229
239
|
log('opening...', { lockKey: this._resourceLock?.lockKey });
|
|
240
|
+
|
|
241
|
+
if (!this._level) {
|
|
242
|
+
this._level = await createLevel(this._config.get('runtime.client.storage', {})!);
|
|
243
|
+
}
|
|
244
|
+
await this._level.open();
|
|
245
|
+
|
|
230
246
|
await this._resourceLock?.acquire();
|
|
231
247
|
|
|
232
248
|
await this._loggingService.open();
|
|
233
249
|
|
|
234
250
|
this._serviceContext = new ServiceContext(
|
|
235
251
|
this._storage,
|
|
252
|
+
this._level,
|
|
236
253
|
this._networkManager,
|
|
237
254
|
this._signalManager,
|
|
238
255
|
this._runtimeParams,
|
|
@@ -248,11 +265,7 @@ export class ClientServicesHost {
|
|
|
248
265
|
(profile) => this._serviceContext.broadcastProfileUpdate(profile),
|
|
249
266
|
),
|
|
250
267
|
|
|
251
|
-
InvitationsService: new InvitationsServiceImpl(
|
|
252
|
-
this._serviceContext.invitations,
|
|
253
|
-
(invitation) => this._serviceContext.getInvitationHandler(invitation),
|
|
254
|
-
this._serviceContext.metadataStore,
|
|
255
|
-
),
|
|
268
|
+
InvitationsService: new InvitationsServiceImpl(this._serviceContext.invitationsManager),
|
|
256
269
|
|
|
257
270
|
DevicesService: new DevicesServiceImpl(this._serviceContext.identityManager),
|
|
258
271
|
|
|
@@ -265,12 +278,8 @@ export class ClientServicesHost {
|
|
|
265
278
|
},
|
|
266
279
|
),
|
|
267
280
|
|
|
268
|
-
DataService:
|
|
269
|
-
|
|
270
|
-
IndexService: new IndexServiceImpl({
|
|
271
|
-
indexer: this._serviceContext.indexer,
|
|
272
|
-
automergeHost: this._serviceContext.automergeHost,
|
|
273
|
-
}),
|
|
281
|
+
DataService: this._serviceContext.echoHost.dataService,
|
|
282
|
+
QueryService: this._serviceContext.echoHost.queryService,
|
|
274
283
|
|
|
275
284
|
NetworkService: new NetworkServiceImpl(this._serviceContext.networkManager, this._serviceContext.signalManager),
|
|
276
285
|
|
|
@@ -286,11 +295,6 @@ export class ClientServicesHost {
|
|
|
286
295
|
});
|
|
287
296
|
|
|
288
297
|
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
298
|
|
|
295
299
|
const devtoolsProxy = this._config?.get('runtime.client.devtoolsProxy');
|
|
296
300
|
if (devtoolsProxy) {
|
|
@@ -302,6 +306,7 @@ export class ClientServicesHost {
|
|
|
302
306
|
});
|
|
303
307
|
void this._devtoolsProxy.open();
|
|
304
308
|
}
|
|
309
|
+
this.diagnosticsBroadcastHandler.start();
|
|
305
310
|
|
|
306
311
|
this._opening = false;
|
|
307
312
|
this._open = true;
|
|
@@ -320,10 +325,12 @@ export class ClientServicesHost {
|
|
|
320
325
|
|
|
321
326
|
const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
|
|
322
327
|
log('closing...', { deviceKey });
|
|
328
|
+
this.diagnosticsBroadcastHandler.stop();
|
|
323
329
|
await this._devtoolsProxy?.close();
|
|
324
330
|
this._serviceRegistry.setServices({ SystemService: this._systemService });
|
|
325
331
|
await this._loggingService.close();
|
|
326
332
|
await this._serviceContext.close();
|
|
333
|
+
await this._level?.close();
|
|
327
334
|
this._open = false;
|
|
328
335
|
this._statusUpdate.emit();
|
|
329
336
|
log('closed', { deviceKey });
|
|
@@ -350,18 +357,27 @@ export class ClientServicesHost {
|
|
|
350
357
|
|
|
351
358
|
const automergeIndex = space.automergeSpaceState.rootUrl;
|
|
352
359
|
invariant(automergeIndex);
|
|
353
|
-
const document = await this._serviceContext.
|
|
360
|
+
const document = await this._serviceContext.echoHost.automergeRepo.find<SpaceDoc>(automergeIndex as any);
|
|
354
361
|
await document.whenReady();
|
|
355
362
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
363
|
+
// TODO(dmaretskyi): Better API for low-level data access.
|
|
364
|
+
const properties: ObjectStructure = {
|
|
365
|
+
system: {
|
|
366
|
+
type: encodeReference(getTypeReference(Properties)!),
|
|
367
|
+
},
|
|
368
|
+
data: {
|
|
369
|
+
[defaultKey]: identity.identityKey.toHex(),
|
|
370
|
+
},
|
|
371
|
+
meta: {
|
|
372
|
+
keys: [],
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
const propertiesId = PublicKey.random().toHex();
|
|
360
376
|
document.change((doc: SpaceDoc) => {
|
|
361
|
-
assignDeep(doc, ['objects',
|
|
377
|
+
assignDeep(doc, ['objects', propertiesId], properties);
|
|
362
378
|
});
|
|
363
|
-
|
|
364
|
-
await
|
|
379
|
+
|
|
380
|
+
await this._serviceContext.echoHost.flush();
|
|
365
381
|
|
|
366
382
|
return identity;
|
|
367
383
|
}
|
|
@@ -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, {
|
|
@@ -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.echoHost, 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.echoHost, peer1.dataSpaceManager, peer2.echoHost, 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.echoHost, peer1.dataSpaceManager, peer2.echoHost, 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.echoHost, 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,18 +4,20 @@
|
|
|
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';
|
|
8
|
-
import { type
|
|
7
|
+
import { getCredentialAssertion, type CredentialSigner, type DelegateInvitationCredential } from '@dxos/credentials';
|
|
8
|
+
import { type EchoHost } from '@dxos/echo-db';
|
|
9
|
+
import { type MetadataStore, type Space, type SpaceManager } from '@dxos/echo-pipeline';
|
|
9
10
|
import { type FeedStore } from '@dxos/feed-store';
|
|
10
11
|
import { invariant } from '@dxos/invariant';
|
|
11
12
|
import { type Keyring } from '@dxos/keyring';
|
|
12
13
|
import { PublicKey } from '@dxos/keys';
|
|
13
14
|
import { log } from '@dxos/log';
|
|
14
15
|
import { trace } from '@dxos/protocols';
|
|
15
|
-
import { SpaceState } from '@dxos/protocols/proto/dxos/client/services';
|
|
16
|
+
import { Invitation, SpaceState } from '@dxos/protocols/proto/dxos/client/services';
|
|
16
17
|
import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
17
18
|
import { type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
18
19
|
import { type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
20
|
+
import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
19
21
|
import { Gossip, Presence } from '@dxos/teleport-extension-gossip';
|
|
20
22
|
import { type Timeframe } from '@dxos/timeframe';
|
|
21
23
|
import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
|
|
@@ -23,6 +25,7 @@ import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
|
|
|
23
25
|
import { DataSpace } from './data-space';
|
|
24
26
|
import { spaceGenesis } from './genesis';
|
|
25
27
|
import { createAuthProvider } from '../identity';
|
|
28
|
+
import { type InvitationsManager } from '../invitations';
|
|
26
29
|
|
|
27
30
|
const PRESENCE_ANNOUNCE_INTERVAL = 10_000;
|
|
28
31
|
const PRESENCE_OFFLINE_TIMEOUT = 20_000;
|
|
@@ -77,7 +80,8 @@ export class DataSpaceManager {
|
|
|
77
80
|
private readonly _keyring: Keyring,
|
|
78
81
|
private readonly _signingContext: SigningContext,
|
|
79
82
|
private readonly _feedStore: FeedStore<FeedMessage>,
|
|
80
|
-
private readonly
|
|
83
|
+
private readonly _echoHost: EchoHost,
|
|
84
|
+
private readonly _invitationsManager: InvitationsManager,
|
|
81
85
|
params?: DataSpaceManagerRuntimeParams,
|
|
82
86
|
) {
|
|
83
87
|
const {
|
|
@@ -149,14 +153,10 @@ export class DataSpaceManager {
|
|
|
149
153
|
|
|
150
154
|
log('creating space...', { spaceKey });
|
|
151
155
|
|
|
152
|
-
const
|
|
153
|
-
automergeRoot.change((doc: any) => {
|
|
154
|
-
doc.access = { spaceKey: spaceKey.toHex() };
|
|
155
|
-
});
|
|
156
|
-
|
|
156
|
+
const automergeRootUrl = await this._echoHost.createSpaceRoot(spaceKey);
|
|
157
157
|
const space = await this._constructSpace(metadata);
|
|
158
158
|
|
|
159
|
-
const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner,
|
|
159
|
+
const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner, automergeRootUrl);
|
|
160
160
|
await this._metadataStore.addSpace(metadata);
|
|
161
161
|
|
|
162
162
|
const memberCredential = credentials[1];
|
|
@@ -240,13 +240,16 @@ export class DataSpaceManager {
|
|
|
240
240
|
gossip.createExtension({ remotePeerId: session.remotePeerId }),
|
|
241
241
|
);
|
|
242
242
|
session.addExtension('dxos.mesh.teleport.notarization', dataSpace.notarizationPlugin.createExtension());
|
|
243
|
-
this.
|
|
244
|
-
session.addExtension('dxos.mesh.teleport.automerge', this.
|
|
243
|
+
this._echoHost.authorizeDevice(space.key, session.remotePeerId);
|
|
244
|
+
session.addExtension('dxos.mesh.teleport.automerge', this._echoHost.createReplicationExtension());
|
|
245
245
|
},
|
|
246
246
|
onAuthFailure: () => {
|
|
247
247
|
log.warn('auth failure');
|
|
248
248
|
},
|
|
249
249
|
memberKey: this._signingContext.identityKey,
|
|
250
|
+
onDelegatedInvitationStatusChange: (invitation, isActive) => {
|
|
251
|
+
return this._handleInvitationStatusChange(dataSpace, invitation, isActive);
|
|
252
|
+
},
|
|
250
253
|
});
|
|
251
254
|
controlFeed && (await space.setControlFeed(controlFeed));
|
|
252
255
|
dataFeed && (await space.setDataFeed(dataFeed));
|
|
@@ -259,6 +262,7 @@ export class DataSpaceManager {
|
|
|
259
262
|
presence,
|
|
260
263
|
keyring: this._keyring,
|
|
261
264
|
feedStore: this._feedStore,
|
|
265
|
+
echoHost: this._echoHost,
|
|
262
266
|
signingContext: this._signingContext,
|
|
263
267
|
callbacks: {
|
|
264
268
|
beforeReady: async () => {
|
|
@@ -267,6 +271,7 @@ export class DataSpaceManager {
|
|
|
267
271
|
afterReady: async () => {
|
|
268
272
|
log('after space ready', { space: space.key, open: this._isOpen });
|
|
269
273
|
if (this._isOpen) {
|
|
274
|
+
await this._createDelegatedInvitations(dataSpace, [...space.spaceState.invitations.entries()]);
|
|
270
275
|
this.updated.emit();
|
|
271
276
|
}
|
|
272
277
|
},
|
|
@@ -275,7 +280,6 @@ export class DataSpaceManager {
|
|
|
275
280
|
},
|
|
276
281
|
},
|
|
277
282
|
cache: metadata.cache,
|
|
278
|
-
automergeHost: this._automergeHost,
|
|
279
283
|
});
|
|
280
284
|
|
|
281
285
|
if (metadata.state !== SpaceState.INACTIVE) {
|
|
@@ -289,4 +293,43 @@ export class DataSpaceManager {
|
|
|
289
293
|
this._spaces.set(metadata.key, dataSpace);
|
|
290
294
|
return dataSpace;
|
|
291
295
|
}
|
|
296
|
+
|
|
297
|
+
private async _handleInvitationStatusChange(
|
|
298
|
+
dataSpace: DataSpace | undefined,
|
|
299
|
+
delegatedInvitation: DelegateInvitationCredential,
|
|
300
|
+
isActive: boolean,
|
|
301
|
+
): Promise<void> {
|
|
302
|
+
if (dataSpace?.state !== SpaceState.READY) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (isActive) {
|
|
306
|
+
await this._createDelegatedInvitations(dataSpace, [
|
|
307
|
+
[delegatedInvitation.credentialId, delegatedInvitation.invitation],
|
|
308
|
+
]);
|
|
309
|
+
} else {
|
|
310
|
+
await this._invitationsManager.cancelInvitation(delegatedInvitation.invitation);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private async _createDelegatedInvitations(
|
|
315
|
+
space: DataSpace,
|
|
316
|
+
invitations: Array<[PublicKey, DelegateSpaceInvitation]>,
|
|
317
|
+
): Promise<void> {
|
|
318
|
+
const tasks = invitations.map(([credentialId, invitation]) => {
|
|
319
|
+
return this._invitationsManager.createInvitation({
|
|
320
|
+
type: Invitation.Type.DELEGATED,
|
|
321
|
+
kind: Invitation.Kind.SPACE,
|
|
322
|
+
spaceKey: space.key,
|
|
323
|
+
authMethod: invitation.authMethod,
|
|
324
|
+
invitationId: invitation.invitationId,
|
|
325
|
+
swarmKey: invitation.swarmKey,
|
|
326
|
+
guestKeypair: invitation.guestKey ? { publicKey: invitation.guestKey } : undefined,
|
|
327
|
+
lifetime: invitation.expiresOn ? invitation.expiresOn.getTime() - Date.now() : undefined,
|
|
328
|
+
multiUse: invitation.multiUse,
|
|
329
|
+
delegationCredentialId: credentialId,
|
|
330
|
+
persistent: false,
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
await Promise.all(tasks);
|
|
334
|
+
}
|
|
292
335
|
}
|
|
@@ -6,15 +6,10 @@ 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 {
|
|
10
|
-
import {
|
|
11
|
-
type MetadataStore,
|
|
12
|
-
type Space,
|
|
13
|
-
createMappedFeedWriter,
|
|
14
|
-
type AutomergeHost,
|
|
15
|
-
type SpaceDoc,
|
|
16
|
-
} from '@dxos/echo-pipeline';
|
|
9
|
+
import { type EchoHost } from '@dxos/echo-db';
|
|
10
|
+
import { type MetadataStore, type Space, createMappedFeedWriter, type SpaceDoc } from '@dxos/echo-pipeline';
|
|
17
11
|
import { AutomergeDocumentLoaderImpl } from '@dxos/echo-pipeline';
|
|
12
|
+
import { TYPE_PROPERTIES } from '@dxos/echo-schema';
|
|
18
13
|
import { type FeedStore } from '@dxos/feed-store';
|
|
19
14
|
import { failedInvariant, invariant } from '@dxos/invariant';
|
|
20
15
|
import { type Keyring } from '@dxos/keyring';
|
|
@@ -66,10 +61,10 @@ export type DataSpaceParams = {
|
|
|
66
61
|
presence: Presence;
|
|
67
62
|
keyring: Keyring;
|
|
68
63
|
feedStore: FeedStore<FeedMessage>;
|
|
64
|
+
echoHost: EchoHost;
|
|
69
65
|
signingContext: SigningContext;
|
|
70
66
|
callbacks?: DataSpaceCallbacks;
|
|
71
67
|
cache?: SpaceCache;
|
|
72
|
-
automergeHost: AutomergeHost;
|
|
73
68
|
};
|
|
74
69
|
|
|
75
70
|
export type CreateEpochOptions = {
|
|
@@ -92,7 +87,7 @@ export class DataSpace {
|
|
|
92
87
|
private readonly _notarizationPlugin = new NotarizationPlugin();
|
|
93
88
|
private readonly _callbacks: DataSpaceCallbacks;
|
|
94
89
|
private readonly _cache?: SpaceCache = undefined;
|
|
95
|
-
private readonly
|
|
90
|
+
private readonly _echoHost: EchoHost;
|
|
96
91
|
|
|
97
92
|
// TODO(dmaretskyi): Move into Space?
|
|
98
93
|
private readonly _automergeSpaceState = new AutomergeSpaceState((rootUrl) => this._onNewAutomergeRoot(rootUrl));
|
|
@@ -120,7 +115,7 @@ export class DataSpace {
|
|
|
120
115
|
this._metadataStore = params.metadataStore;
|
|
121
116
|
this._signingContext = params.signingContext;
|
|
122
117
|
this._callbacks = params.callbacks ?? {};
|
|
123
|
-
this.
|
|
118
|
+
this._echoHost = params.echoHost;
|
|
124
119
|
|
|
125
120
|
this.authVerifier = new TrustedKeySetAuthVerifier({
|
|
126
121
|
trustedKeysProvider: () =>
|
|
@@ -363,8 +358,8 @@ export class DataSpace {
|
|
|
363
358
|
|
|
364
359
|
private _onNewAutomergeRoot(rootUrl: string) {
|
|
365
360
|
log('loading automerge root doc for space', { space: this.key, rootUrl });
|
|
366
|
-
this.
|
|
367
|
-
const handle = this.
|
|
361
|
+
this._echoHost.replicateDocument(rootUrl);
|
|
362
|
+
const handle = this._echoHost.automergeRepo.find(rootUrl as any);
|
|
368
363
|
|
|
369
364
|
queueMicrotask(async () => {
|
|
370
365
|
try {
|
|
@@ -419,7 +414,7 @@ export class DataSpace {
|
|
|
419
414
|
break;
|
|
420
415
|
case CreateEpochRequest.Migration.INIT_AUTOMERGE:
|
|
421
416
|
{
|
|
422
|
-
const document = this.
|
|
417
|
+
const document = this._echoHost.automergeRepo.create();
|
|
423
418
|
// TODO(dmaretskyi): Unify epoch construction.
|
|
424
419
|
epoch = {
|
|
425
420
|
previousId: this._automergeSpaceState.lastEpoch?.id,
|
|
@@ -432,9 +427,9 @@ export class DataSpace {
|
|
|
432
427
|
case CreateEpochRequest.Migration.PRUNE_AUTOMERGE_ROOT_HISTORY:
|
|
433
428
|
{
|
|
434
429
|
const currentRootUrl = this._automergeSpaceState.rootUrl;
|
|
435
|
-
const rootHandle = this.
|
|
430
|
+
const rootHandle = this._echoHost.automergeRepo.find(currentRootUrl as any);
|
|
436
431
|
await cancelWithContext(this._ctx, asyncTimeout(rootHandle.whenReady(), 10_000));
|
|
437
|
-
const newRoot = this.
|
|
432
|
+
const newRoot = this._echoHost.automergeRepo.create(rootHandle.docSync());
|
|
438
433
|
invariant(typeof newRoot.url === 'string' && newRoot.url.length > 0);
|
|
439
434
|
// TODO(dmaretskyi): Unify epoch construction.
|
|
440
435
|
epoch = {
|
|
@@ -450,7 +445,7 @@ export class DataSpace {
|
|
|
450
445
|
log.info('Fragmenting');
|
|
451
446
|
|
|
452
447
|
const currentRootUrl = this._automergeSpaceState.rootUrl;
|
|
453
|
-
const rootHandle = this.
|
|
448
|
+
const rootHandle = this._echoHost.automergeRepo.find<SpaceDoc>(currentRootUrl as any);
|
|
454
449
|
await cancelWithContext(this._ctx, asyncTimeout(rootHandle.whenReady(), 10_000));
|
|
455
450
|
|
|
456
451
|
// Find properties object.
|
|
@@ -461,11 +456,11 @@ export class DataSpace {
|
|
|
461
456
|
|
|
462
457
|
// Create a new space doc with the properties object.
|
|
463
458
|
const newSpaceDoc: SpaceDoc = { ...rootHandle.docSync(), objects: Object.fromEntries([properties]) };
|
|
464
|
-
const newRoot = this.
|
|
459
|
+
const newRoot = this._echoHost.automergeRepo.create(newSpaceDoc);
|
|
465
460
|
invariant(typeof newRoot.url === 'string' && newRoot.url.length > 0);
|
|
466
461
|
|
|
467
462
|
// Create new automerge documents for all objects.
|
|
468
|
-
const docLoader = new AutomergeDocumentLoaderImpl(this.key, this.
|
|
463
|
+
const docLoader = new AutomergeDocumentLoaderImpl(this.key, this._echoHost.automergeRepo);
|
|
469
464
|
await docLoader.loadSpaceRootDocHandle(this._ctx, { rootUrl: newRoot.url });
|
|
470
465
|
|
|
471
466
|
otherObjects.forEach(([key, value]) => {
|
|
@@ -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();
|
|
@@ -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:
|
|
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 '../
|
|
19
|
+
import { type Diagnostics } from '../diagnostics';
|
|
20
20
|
import { getPlatform } from '../services/platform';
|
|
21
21
|
|
|
22
22
|
export type SystemServiceOptions = {
|