@dxos/client-services 0.4.10-main.fa5a270 → 0.4.10-main.fd8ea31
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-7PYX6UUA.mjs → chunk-WLE7E36I.mjs} +1518 -1077
- package/dist/lib/browser/chunk-WLE7E36I.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +62 -22
- 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 +136 -116
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-4TX623I7.cjs → chunk-YXZQQAQN.cjs} +1409 -1056
- package/dist/lib/node/chunk-YXZQQAQN.cjs.map +7 -0
- package/dist/lib/node/index.cjs +98 -58
- 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 +135 -118
- 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/indexing/util.d.ts +2 -6
- package/dist/types/src/packlets/indexing/util.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 +9 -5
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +6 -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 +5 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- 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 +7 -2
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/shared-worker-connection.d.ts +5 -5
- package/dist/types/src/packlets/vault/shared-worker-connection.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-runtime.d.ts +2 -0
- package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-session.d.ts +2 -0
- package/dist/types/src/packlets/vault/worker-session.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/indexing/util.ts +11 -68
- 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 +10 -4
- 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 +30 -11
- package/src/packlets/services/service-host.ts +63 -24
- 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 +48 -2
- package/src/packlets/spaces/data-space.ts +51 -2
- 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 +42 -6
- package/src/packlets/vault/shared-worker-connection.ts +3 -8
- package/src/packlets/vault/worker-runtime.ts +27 -2
- package/src/packlets/vault/worker-session.ts +6 -0
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-7PYX6UUA.mjs.map +0 -7
- package/dist/lib/node/chunk-4TX623I7.cjs.map +0 -7
- package/dist/types/src/packlets/services/diagnostics.d.ts.map +0 -1
|
@@ -2,13 +2,21 @@
|
|
|
2
2
|
// Copyright 2021 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { type Level } from 'level';
|
|
6
|
+
|
|
5
7
|
import { Event, synchronized } from '@dxos/async';
|
|
6
|
-
import {
|
|
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
|
-
|
|
11
|
-
|
|
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';
|
|
12
20
|
import { invariant } from '@dxos/invariant';
|
|
13
21
|
import { PublicKey } from '@dxos/keys';
|
|
14
22
|
import { log } from '@dxos/log';
|
|
@@ -21,18 +29,22 @@ import { TRACE_PROCESSOR, trace as Trace } from '@dxos/tracing';
|
|
|
21
29
|
import { assignDeep } from '@dxos/util';
|
|
22
30
|
import { WebsocketRpcClient } from '@dxos/websocket-rpc';
|
|
23
31
|
|
|
24
|
-
import { createDiagnostics } from './diagnostics';
|
|
25
32
|
import { ServiceContext, type ServiceContextRuntimeParams } from './service-context';
|
|
26
33
|
import { ServiceRegistry } from './service-registry';
|
|
27
34
|
import { DevicesServiceImpl } from '../devices';
|
|
28
35
|
import { DevtoolsHostEvents, DevtoolsServiceImpl } from '../devtools';
|
|
36
|
+
import {
|
|
37
|
+
type CollectDiagnosticsBroadcastHandler,
|
|
38
|
+
createCollectDiagnosticsBroadcastHandler,
|
|
39
|
+
createDiagnostics,
|
|
40
|
+
} from '../diagnostics';
|
|
29
41
|
import { IdentityServiceImpl, type CreateIdentityOptions } from '../identity';
|
|
30
42
|
import { InvitationsServiceImpl } from '../invitations';
|
|
31
43
|
import { Lock, type ResourceLock } from '../locks';
|
|
32
44
|
import { LoggingServiceImpl } from '../logging';
|
|
33
45
|
import { NetworkServiceImpl } from '../network';
|
|
34
46
|
import { SpacesServiceImpl } from '../spaces';
|
|
35
|
-
import { createStorageObjects } from '../storage';
|
|
47
|
+
import { createLevel, createStorageObjects } from '../storage';
|
|
36
48
|
import { SystemServiceImpl } from '../system';
|
|
37
49
|
|
|
38
50
|
export type ClientServicesHostParams = {
|
|
@@ -44,6 +56,7 @@ export type ClientServicesHostParams = {
|
|
|
44
56
|
signalManager?: SignalManager;
|
|
45
57
|
connectionLog?: boolean;
|
|
46
58
|
storage?: Storage;
|
|
59
|
+
level?: LevelDB;
|
|
47
60
|
lockKey?: string;
|
|
48
61
|
callbacks?: ClientServicesHostCallbacks;
|
|
49
62
|
runtimeParams?: ServiceContextRuntimeParams;
|
|
@@ -70,17 +83,20 @@ export class ClientServicesHost {
|
|
|
70
83
|
private readonly _systemService: SystemServiceImpl;
|
|
71
84
|
private readonly _loggingService: LoggingServiceImpl;
|
|
72
85
|
private readonly _tracingService = TRACE_PROCESSOR.createTraceSender();
|
|
86
|
+
private _queryService!: QueryServiceImpl;
|
|
73
87
|
|
|
74
88
|
private _config?: Config;
|
|
75
89
|
private readonly _statusUpdate = new Event<void>();
|
|
76
90
|
private _signalManager?: SignalManager;
|
|
77
91
|
private _networkManager?: NetworkManager;
|
|
78
92
|
private _storage?: Storage;
|
|
93
|
+
private _level?: Level<string, string>;
|
|
79
94
|
private _callbacks?: ClientServicesHostCallbacks;
|
|
80
95
|
private _devtoolsProxy?: WebsocketRpcClient<{}, ClientServices>;
|
|
81
96
|
|
|
82
97
|
private _serviceContext!: ServiceContext;
|
|
83
98
|
private readonly _runtimeParams?: ServiceContextRuntimeParams;
|
|
99
|
+
private diagnosticsBroadcastHandler: CollectDiagnosticsBroadcastHandler;
|
|
84
100
|
|
|
85
101
|
@Trace.info()
|
|
86
102
|
private _opening = false;
|
|
@@ -93,12 +109,14 @@ export class ClientServicesHost {
|
|
|
93
109
|
transportFactory,
|
|
94
110
|
signalManager,
|
|
95
111
|
storage,
|
|
112
|
+
level,
|
|
96
113
|
// TODO(wittjosiah): Turn this on by default.
|
|
97
114
|
lockKey,
|
|
98
115
|
callbacks,
|
|
99
116
|
runtimeParams,
|
|
100
117
|
}: ClientServicesHostParams = {}) {
|
|
101
118
|
this._storage = storage;
|
|
119
|
+
this._level = level;
|
|
102
120
|
this._callbacks = callbacks;
|
|
103
121
|
this._runtimeParams = runtimeParams;
|
|
104
122
|
|
|
@@ -138,6 +156,7 @@ export class ClientServicesHost {
|
|
|
138
156
|
},
|
|
139
157
|
});
|
|
140
158
|
|
|
159
|
+
this.diagnosticsBroadcastHandler = createCollectDiagnosticsBroadcastHandler(this._systemService);
|
|
141
160
|
this._loggingService = new LoggingServiceImpl();
|
|
142
161
|
|
|
143
162
|
this._serviceRegistry = new ServiceRegistry<ClientServices>(clientServiceBundle, {
|
|
@@ -187,6 +206,9 @@ export class ClientServicesHost {
|
|
|
187
206
|
}
|
|
188
207
|
}
|
|
189
208
|
|
|
209
|
+
if (!options.signalManager) {
|
|
210
|
+
log.warn('running signaling without telemetry metadata.');
|
|
211
|
+
}
|
|
190
212
|
const {
|
|
191
213
|
connectionLog = true,
|
|
192
214
|
transportFactory = createSimplePeerTransportFactory({
|
|
@@ -223,17 +245,30 @@ export class ClientServicesHost {
|
|
|
223
245
|
|
|
224
246
|
this._opening = true;
|
|
225
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
|
+
|
|
226
254
|
await this._resourceLock?.acquire();
|
|
227
255
|
|
|
228
256
|
await this._loggingService.open();
|
|
229
257
|
|
|
230
258
|
this._serviceContext = new ServiceContext(
|
|
231
259
|
this._storage,
|
|
260
|
+
this._level,
|
|
232
261
|
this._networkManager,
|
|
233
262
|
this._signalManager,
|
|
234
263
|
this._runtimeParams,
|
|
235
264
|
);
|
|
236
265
|
|
|
266
|
+
this._queryService = new QueryServiceImpl({
|
|
267
|
+
indexer: this._serviceContext.indexer,
|
|
268
|
+
automergeHost: this._serviceContext.automergeHost,
|
|
269
|
+
});
|
|
270
|
+
await this._queryService.open(ctx);
|
|
271
|
+
|
|
237
272
|
this._serviceRegistry.setServices({
|
|
238
273
|
SystemService: this._systemService,
|
|
239
274
|
|
|
@@ -244,11 +279,7 @@ export class ClientServicesHost {
|
|
|
244
279
|
(profile) => this._serviceContext.broadcastProfileUpdate(profile),
|
|
245
280
|
),
|
|
246
281
|
|
|
247
|
-
InvitationsService: new InvitationsServiceImpl(
|
|
248
|
-
this._serviceContext.invitations,
|
|
249
|
-
(invitation) => this._serviceContext.getInvitationHandler(invitation),
|
|
250
|
-
this._serviceContext.metadataStore,
|
|
251
|
-
),
|
|
282
|
+
InvitationsService: new InvitationsServiceImpl(this._serviceContext.invitationsManager),
|
|
252
283
|
|
|
253
284
|
DevicesService: new DevicesServiceImpl(this._serviceContext.identityManager),
|
|
254
285
|
|
|
@@ -263,10 +294,7 @@ export class ClientServicesHost {
|
|
|
263
294
|
|
|
264
295
|
DataService: new DataServiceImpl(this._serviceContext.automergeHost),
|
|
265
296
|
|
|
266
|
-
|
|
267
|
-
indexer: this._serviceContext.indexer,
|
|
268
|
-
automergeHost: this._serviceContext.automergeHost,
|
|
269
|
-
}),
|
|
297
|
+
QueryService: this._queryService,
|
|
270
298
|
|
|
271
299
|
NetworkService: new NetworkServiceImpl(this._serviceContext.networkManager, this._serviceContext.signalManager),
|
|
272
300
|
|
|
@@ -282,11 +310,6 @@ export class ClientServicesHost {
|
|
|
282
310
|
});
|
|
283
311
|
|
|
284
312
|
await this._serviceContext.open(ctx);
|
|
285
|
-
// TODO(nf): move to InvitationManager in ServiceContext?
|
|
286
|
-
invariant(this.serviceRegistry.services.InvitationsService);
|
|
287
|
-
const loadedInvitations = await this.serviceRegistry.services.InvitationsService.loadPersistentInvitations();
|
|
288
|
-
|
|
289
|
-
log('loaded persistent invitations', { count: loadedInvitations.invitations?.length });
|
|
290
313
|
|
|
291
314
|
const devtoolsProxy = this._config?.get('runtime.client.devtoolsProxy');
|
|
292
315
|
if (devtoolsProxy) {
|
|
@@ -298,6 +321,7 @@ export class ClientServicesHost {
|
|
|
298
321
|
});
|
|
299
322
|
void this._devtoolsProxy.open();
|
|
300
323
|
}
|
|
324
|
+
this.diagnosticsBroadcastHandler.start();
|
|
301
325
|
|
|
302
326
|
this._opening = false;
|
|
303
327
|
this._open = true;
|
|
@@ -316,10 +340,13 @@ export class ClientServicesHost {
|
|
|
316
340
|
|
|
317
341
|
const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
|
|
318
342
|
log('closing...', { deviceKey });
|
|
343
|
+
this.diagnosticsBroadcastHandler.stop();
|
|
319
344
|
await this._devtoolsProxy?.close();
|
|
320
345
|
this._serviceRegistry.setServices({ SystemService: this._systemService });
|
|
321
346
|
await this._loggingService.close();
|
|
347
|
+
await this._queryService.close();
|
|
322
348
|
await this._serviceContext.close();
|
|
349
|
+
await this._level?.close();
|
|
323
350
|
this._open = false;
|
|
324
351
|
this._statusUpdate.emit();
|
|
325
352
|
log('closed', { deviceKey });
|
|
@@ -344,18 +371,30 @@ export class ClientServicesHost {
|
|
|
344
371
|
await this._serviceContext.initialized.wait();
|
|
345
372
|
const space = await this._serviceContext.dataSpaceManager!.createSpace();
|
|
346
373
|
|
|
347
|
-
const obj: TypedObject = new Properties(undefined);
|
|
348
|
-
obj[defaultKey] = identity.identityKey.toHex();
|
|
349
|
-
|
|
350
374
|
const automergeIndex = space.automergeSpaceState.rootUrl;
|
|
351
375
|
invariant(automergeIndex);
|
|
352
376
|
const document = await this._serviceContext.automergeHost.repo.find<SpaceDoc>(automergeIndex as any);
|
|
353
377
|
await document.whenReady();
|
|
354
378
|
|
|
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();
|
|
355
392
|
document.change((doc: SpaceDoc) => {
|
|
356
|
-
assignDeep(doc, ['objects',
|
|
393
|
+
assignDeep(doc, ['objects', propertiesId], properties);
|
|
357
394
|
});
|
|
358
395
|
|
|
396
|
+
await this._serviceContext.automergeHost.repo.flush();
|
|
397
|
+
|
|
359
398
|
return identity;
|
|
360
399
|
}
|
|
361
400
|
}
|
|
@@ -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.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,15 @@ 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 {
|
|
9
|
+
import {
|
|
10
|
+
type MetadataStore,
|
|
11
|
+
type Space,
|
|
12
|
+
createMappedFeedWriter,
|
|
13
|
+
type AutomergeHost,
|
|
14
|
+
type SpaceDoc,
|
|
15
|
+
} from '@dxos/echo-pipeline';
|
|
16
|
+
import { AutomergeDocumentLoaderImpl } from '@dxos/echo-pipeline';
|
|
17
|
+
import { TYPE_PROPERTIES } from '@dxos/echo-schema';
|
|
10
18
|
import { type FeedStore } from '@dxos/feed-store';
|
|
11
19
|
import { failedInvariant, invariant } from '@dxos/invariant';
|
|
12
20
|
import { type Keyring } from '@dxos/keyring';
|
|
@@ -26,7 +34,7 @@ import { type GossipMessage } from '@dxos/protocols/proto/dxos/mesh/teleport/gos
|
|
|
26
34
|
import { type Gossip, type Presence } from '@dxos/teleport-extension-gossip';
|
|
27
35
|
import { Timeframe } from '@dxos/timeframe';
|
|
28
36
|
import { trace } from '@dxos/tracing';
|
|
29
|
-
import { ComplexSet } from '@dxos/util';
|
|
37
|
+
import { ComplexSet, assignDeep } from '@dxos/util';
|
|
30
38
|
|
|
31
39
|
import { AutomergeSpaceState } from './automerge-space-state';
|
|
32
40
|
import { type SigningContext } from './data-space-manager';
|
|
@@ -437,6 +445,47 @@ export class DataSpace {
|
|
|
437
445
|
};
|
|
438
446
|
}
|
|
439
447
|
break;
|
|
448
|
+
case CreateEpochRequest.Migration.FRAGMENT_AUTOMERGE_ROOT:
|
|
449
|
+
{
|
|
450
|
+
log.info('Fragmenting');
|
|
451
|
+
|
|
452
|
+
const currentRootUrl = this._automergeSpaceState.rootUrl;
|
|
453
|
+
const rootHandle = this._automergeHost.repo.find<SpaceDoc>(currentRootUrl as any);
|
|
454
|
+
await cancelWithContext(this._ctx, asyncTimeout(rootHandle.whenReady(), 10_000));
|
|
455
|
+
|
|
456
|
+
// Find properties object.
|
|
457
|
+
const objects = Object.entries((rootHandle.docSync() as SpaceDoc).objects!);
|
|
458
|
+
const properties = objects.find(([_, value]) => value.system.type?.itemId === TYPE_PROPERTIES);
|
|
459
|
+
const otherObjects = objects.filter(([key]) => key !== properties?.[0]);
|
|
460
|
+
invariant(properties, 'Properties not found');
|
|
461
|
+
|
|
462
|
+
// Create a new space doc with the properties object.
|
|
463
|
+
const newSpaceDoc: SpaceDoc = { ...rootHandle.docSync(), objects: Object.fromEntries([properties]) };
|
|
464
|
+
const newRoot = this._automergeHost.repo.create(newSpaceDoc);
|
|
465
|
+
invariant(typeof newRoot.url === 'string' && newRoot.url.length > 0);
|
|
466
|
+
|
|
467
|
+
// Create new automerge documents for all objects.
|
|
468
|
+
const docLoader = new AutomergeDocumentLoaderImpl(this.key, this._automergeHost.repo);
|
|
469
|
+
await docLoader.loadSpaceRootDocHandle(this._ctx, { rootUrl: newRoot.url });
|
|
470
|
+
|
|
471
|
+
otherObjects.forEach(([key, value]) => {
|
|
472
|
+
const handle = docLoader.createDocumentForObject(key);
|
|
473
|
+
handle.change((doc: any) => {
|
|
474
|
+
assignDeep(doc, ['objects', key], value);
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// TODO(mykola): Delete old root.
|
|
479
|
+
|
|
480
|
+
// TODO(dmaretskyi): Unify epoch construction.
|
|
481
|
+
epoch = {
|
|
482
|
+
previousId: this._automergeSpaceState.lastEpoch?.id,
|
|
483
|
+
number: (this._automergeSpaceState.lastEpoch?.subject.assertion.number ?? -1) + 1,
|
|
484
|
+
timeframe: this._automergeSpaceState.lastEpoch?.subject.assertion.timeframe ?? new Timeframe(),
|
|
485
|
+
automergeRoot: newRoot.url,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
break;
|
|
440
489
|
}
|
|
441
490
|
|
|
442
491
|
if (!epoch) {
|
|
@@ -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 = {
|