@dxos/client-services 0.4.10-main.3ba6dcf → 0.4.10-main.3c620fb
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-WQ6ASRLV.mjs → chunk-H2TNTLUE.mjs} +707 -515
- package/dist/lib/browser/chunk-H2TNTLUE.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +11 -1
- package/dist/lib/browser/index.mjs.map +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +11 -5
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-U6EYP6CO.cjs → chunk-OSCHFTM3.cjs} +641 -536
- package/dist/lib/node/chunk-OSCHFTM3.cjs.map +7 -0
- package/dist/lib/node/index.cjs +47 -37
- package/dist/lib/node/index.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +15 -9
- 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/indexing/util.d.ts +2 -2
- package/dist/types/src/packlets/indexing/util.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 +7 -5
- 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/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/test-builder.d.ts +5 -2
- package/dist/types/src/packlets/testing/test-builder.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-service.test.ts +1 -1
- package/src/packlets/indexing/util.ts +4 -4
- package/src/packlets/invitations/device-invitation-protocol.test.ts +1 -1
- package/src/packlets/network/network-service.test.ts +1 -1
- package/src/packlets/services/automerge-host.test.ts +9 -3
- 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 +15 -8
- package/src/packlets/services/service-host.ts +49 -9
- 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/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/test-builder.ts +23 -5
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-WQ6ASRLV.mjs.map +0 -7
- package/dist/lib/node/chunk-U6EYP6CO.cjs.map +0 -7
- package/dist/types/src/packlets/services/diagnostics.d.ts.map +0 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Trigger } from '@dxos/async';
|
|
6
|
+
import { log } from '@dxos/log';
|
|
7
|
+
import { type SystemService } from '@dxos/protocols/proto/dxos/client/services';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
type CollectDiagnosticsBroadcastSender,
|
|
11
|
+
type CollectDiagnosticsBroadcastHandler,
|
|
12
|
+
} from './diagnostics-collector';
|
|
13
|
+
|
|
14
|
+
const CHANNEL_NAME = 'dxos.diagnostics.broadcast';
|
|
15
|
+
|
|
16
|
+
enum MessageType {
|
|
17
|
+
PROBE = 'probe',
|
|
18
|
+
PROBE_ACK = 'probe-ack',
|
|
19
|
+
REQUEST_DIAGNOSTICS = 'request-diagnostics',
|
|
20
|
+
RECEIVE_DIAGNOSTICS = 'receive-diagnostics',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Message {
|
|
24
|
+
type: MessageType;
|
|
25
|
+
payload?: any;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const createCollectDiagnosticsBroadcastSender = (): CollectDiagnosticsBroadcastSender => {
|
|
29
|
+
return {
|
|
30
|
+
broadcastDiagnosticsRequest: async () => {
|
|
31
|
+
let expectedResponse = MessageType.PROBE_ACK;
|
|
32
|
+
let channel: BroadcastChannel | undefined;
|
|
33
|
+
try {
|
|
34
|
+
const trigger = new Trigger<Message>();
|
|
35
|
+
channel = new BroadcastChannel(CHANNEL_NAME);
|
|
36
|
+
channel.onmessage = (msg) => {
|
|
37
|
+
if (expectedResponse === msg.data.type) {
|
|
38
|
+
trigger.wake(msg.data);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
channel.postMessage({ type: MessageType.PROBE });
|
|
42
|
+
await trigger.wait({ timeout: 200 });
|
|
43
|
+
expectedResponse = MessageType.RECEIVE_DIAGNOSTICS;
|
|
44
|
+
trigger.reset();
|
|
45
|
+
channel.postMessage({ type: MessageType.REQUEST_DIAGNOSTICS });
|
|
46
|
+
const diagnostics = await trigger.wait({ timeout: 5000 });
|
|
47
|
+
return diagnostics.payload;
|
|
48
|
+
} catch (e) {
|
|
49
|
+
const errorDescription = e instanceof Error ? e.message : JSON.stringify(e);
|
|
50
|
+
return { expectedResponse, errorDescription };
|
|
51
|
+
} finally {
|
|
52
|
+
safeClose(channel);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const createCollectDiagnosticsBroadcastHandler = (
|
|
59
|
+
systemService: SystemService,
|
|
60
|
+
): CollectDiagnosticsBroadcastHandler => {
|
|
61
|
+
let channel: BroadcastChannel | undefined;
|
|
62
|
+
return {
|
|
63
|
+
start: () => {
|
|
64
|
+
channel = new BroadcastChannel(CHANNEL_NAME);
|
|
65
|
+
channel.onmessage = async (message) => {
|
|
66
|
+
try {
|
|
67
|
+
if (message.data.type === MessageType.PROBE) {
|
|
68
|
+
channel?.postMessage({ type: MessageType.PROBE_ACK });
|
|
69
|
+
} else if (message.data.type === MessageType.REQUEST_DIAGNOSTICS) {
|
|
70
|
+
const diagnostics = await systemService.getDiagnostics({});
|
|
71
|
+
channel?.postMessage({
|
|
72
|
+
type: MessageType.RECEIVE_DIAGNOSTICS,
|
|
73
|
+
payload: diagnostics,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
log.catch(error);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
stop: () => {
|
|
82
|
+
safeClose(channel);
|
|
83
|
+
channel = undefined;
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const safeClose = (channel?: BroadcastChannel) => {
|
|
89
|
+
try {
|
|
90
|
+
channel?.close();
|
|
91
|
+
} catch (e) {
|
|
92
|
+
// ignored
|
|
93
|
+
}
|
|
94
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
import { type SystemService } from '@dxos/protocols/proto/dxos/client/services';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type CollectDiagnosticsBroadcastSender,
|
|
8
|
+
type CollectDiagnosticsBroadcastHandler,
|
|
9
|
+
} from './diagnostics-collector';
|
|
10
|
+
|
|
11
|
+
export const createCollectDiagnosticsBroadcastSender = (): CollectDiagnosticsBroadcastSender => {
|
|
12
|
+
return { broadcastDiagnosticsRequest: async () => undefined };
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const createCollectDiagnosticsBroadcastHandler = (_: SystemService): CollectDiagnosticsBroadcastHandler => {
|
|
16
|
+
return {
|
|
17
|
+
start: () => {},
|
|
18
|
+
stop: () => {},
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type ClientServicesProvider } from '@dxos/client-protocol';
|
|
6
|
+
import { type Config, ConfigResource } from '@dxos/config';
|
|
7
|
+
import { GetDiagnosticsRequest } from '@dxos/protocols/proto/dxos/client/services';
|
|
8
|
+
import { TRACE_PROCESSOR } from '@dxos/tracing';
|
|
9
|
+
import { type JsonKeyOptions, jsonKeyReplacer, nonNullable } from '@dxos/util';
|
|
10
|
+
|
|
11
|
+
import { createCollectDiagnosticsBroadcastSender } from './diagnostics-broadcast';
|
|
12
|
+
import { ClientServicesProviderResource } from '../services';
|
|
13
|
+
|
|
14
|
+
export interface CollectDiagnosticsBroadcastSender {
|
|
15
|
+
broadcastDiagnosticsRequest(): any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface CollectDiagnosticsBroadcastHandler {
|
|
19
|
+
start(): void;
|
|
20
|
+
stop(): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class DiagnosticsCollector {
|
|
24
|
+
private static broadcastSender = createCollectDiagnosticsBroadcastSender();
|
|
25
|
+
|
|
26
|
+
public static async collect(
|
|
27
|
+
config: Config | Config[] = findConfigs(),
|
|
28
|
+
services: ClientServicesProvider | null = findSystemServiceProvider(),
|
|
29
|
+
options: JsonKeyOptions = {},
|
|
30
|
+
): Promise<any> {
|
|
31
|
+
const serviceDiagnostics = await services?.services?.SystemService?.getDiagnostics({
|
|
32
|
+
keys: options.humanize
|
|
33
|
+
? GetDiagnosticsRequest.KEY_OPTION.HUMANIZE
|
|
34
|
+
: options.truncate
|
|
35
|
+
? GetDiagnosticsRequest.KEY_OPTION.TRUNCATE
|
|
36
|
+
: undefined,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const clientDiagnostics = {
|
|
40
|
+
config,
|
|
41
|
+
trace: TRACE_PROCESSOR.getDiagnostics(),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const diagnostics =
|
|
45
|
+
serviceDiagnostics != null
|
|
46
|
+
? { client: clientDiagnostics, services: serviceDiagnostics }
|
|
47
|
+
: {
|
|
48
|
+
client: clientDiagnostics,
|
|
49
|
+
broadcast: await this.broadcastSender.broadcastDiagnosticsRequest(),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return JSON.parse(JSON.stringify(diagnostics, jsonKeyReplacer(options)));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const findSystemServiceProvider = (): ClientServicesProvider | null => {
|
|
57
|
+
const serviceProviders = TRACE_PROCESSOR.findByAnnotation(ClientServicesProviderResource);
|
|
58
|
+
const providerResource = serviceProviders.find((r) => r.instance.deref()?.services?.SystemService != null);
|
|
59
|
+
return providerResource?.instance?.deref() ?? null;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const findConfigs = (): Config[] => {
|
|
63
|
+
const configs = TRACE_PROCESSOR.findByAnnotation(ConfigResource);
|
|
64
|
+
return configs.map((r) => r.instance.deref()).filter(nonNullable);
|
|
65
|
+
};
|
|
@@ -25,9 +25,9 @@ import { type Epoch } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
|
25
25
|
import { type Resource, type Span } from '@dxos/protocols/proto/dxos/tracing';
|
|
26
26
|
import { TRACE_PROCESSOR } from '@dxos/tracing';
|
|
27
27
|
|
|
28
|
-
import { getPlatform } from './platform';
|
|
29
|
-
import { type ServiceContext } from './service-context';
|
|
30
28
|
import { DXOS_VERSION } from '../../version';
|
|
29
|
+
import { type ServiceContext } from '../services';
|
|
30
|
+
import { getPlatform } from '../services/platform';
|
|
31
31
|
import { type DataSpace } from '../spaces';
|
|
32
32
|
|
|
33
33
|
const DEFAULT_TIMEOUT = 1_000;
|
|
@@ -22,7 +22,7 @@ describe('IdentityService', () => {
|
|
|
22
22
|
let identityService: IdentityService;
|
|
23
23
|
|
|
24
24
|
beforeEach(async () => {
|
|
25
|
-
serviceContext = createServiceContext();
|
|
25
|
+
serviceContext = await createServiceContext();
|
|
26
26
|
await serviceContext.open(new Context());
|
|
27
27
|
identityService = new IdentityServiceImpl(
|
|
28
28
|
(options) => serviceContext.createIdentity(options),
|
|
@@ -12,7 +12,7 @@ import { idCodec } from '@dxos/protocols';
|
|
|
12
12
|
/**
|
|
13
13
|
* Factory for `loadDocuments` iterator.
|
|
14
14
|
*/
|
|
15
|
-
export const
|
|
15
|
+
export const createSelectedDocumentsIterator = (automergeHost: AutomergeHost) =>
|
|
16
16
|
/**
|
|
17
17
|
* Get object data blobs from Automerge Repo by ids.
|
|
18
18
|
* @param ids
|
|
@@ -25,14 +25,14 @@ export const createLoadDocuments = (automergeHost: AutomergeHost) =>
|
|
|
25
25
|
await warnAfterTimeout(5000, 'to long to load doc', () => handle.whenReady());
|
|
26
26
|
const doc = handle.docSync();
|
|
27
27
|
const hash = getHeads(doc).join('');
|
|
28
|
-
yield [{ id, object: doc.objects[objectId], currentHash: hash }];
|
|
28
|
+
yield doc.objects?.[objectId] ? [{ id, object: doc.objects[objectId], currentHash: hash }] : [];
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Factory for `getAllDocuments` iterator.
|
|
34
34
|
*/
|
|
35
|
-
export const
|
|
35
|
+
export const createDocumentsIterator = (automergeHost: AutomergeHost) =>
|
|
36
36
|
/**
|
|
37
37
|
* Recursively get all object data blobs from Automerge Repo.
|
|
38
38
|
* @param ids
|
|
@@ -57,7 +57,7 @@ export const createGetAllDocuments = (automergeHost: AutomergeHost) =>
|
|
|
57
57
|
return {
|
|
58
58
|
id: idCodec.encode({ documentId: handle.documentId, objectId }),
|
|
59
59
|
object,
|
|
60
|
-
currentHash: heads.
|
|
60
|
+
currentHash: heads.join(''),
|
|
61
61
|
};
|
|
62
62
|
});
|
|
63
63
|
}
|
|
@@ -20,7 +20,7 @@ const closeAfterTest = async (peer: ServiceContext) => {
|
|
|
20
20
|
|
|
21
21
|
describe('services/device', () => {
|
|
22
22
|
test('creates identity', async () => {
|
|
23
|
-
const peer = createServiceContext();
|
|
23
|
+
const peer = await createServiceContext();
|
|
24
24
|
await peer.open(new Context());
|
|
25
25
|
afterTest(() => peer.close());
|
|
26
26
|
|
|
@@ -18,7 +18,7 @@ describe('NetworkService', () => {
|
|
|
18
18
|
let networkService: NetworkService;
|
|
19
19
|
|
|
20
20
|
beforeEach(async () => {
|
|
21
|
-
serviceContext = createServiceContext();
|
|
21
|
+
serviceContext = await createServiceContext();
|
|
22
22
|
await serviceContext.open(new Context());
|
|
23
23
|
networkService = new NetworkServiceImpl(serviceContext.networkManager, serviceContext.signalManager);
|
|
24
24
|
});
|
|
@@ -6,8 +6,8 @@ import { expect } from 'chai';
|
|
|
6
6
|
|
|
7
7
|
import { asyncTimeout, sleep } from '@dxos/async';
|
|
8
8
|
import { AutomergeHost, DataServiceImpl } from '@dxos/echo-pipeline';
|
|
9
|
+
import { createTestLevel } from '@dxos/echo-pipeline/testing';
|
|
9
10
|
import { AutomergeContext } from '@dxos/echo-schema';
|
|
10
|
-
import { StorageType, createStorage } from '@dxos/random-access-storage';
|
|
11
11
|
import { afterTest, describe, test } from '@dxos/test';
|
|
12
12
|
|
|
13
13
|
describe('AutomergeHost', () => {
|
|
@@ -19,10 +19,16 @@ describe('AutomergeHost', () => {
|
|
|
19
19
|
// creates repo and document | replicates repo | finds document in repo
|
|
20
20
|
//
|
|
21
21
|
|
|
22
|
-
const
|
|
22
|
+
const level = createTestLevel();
|
|
23
|
+
await level.open();
|
|
24
|
+
afterTest(() => level.close());
|
|
23
25
|
|
|
24
|
-
const host = new AutomergeHost({
|
|
26
|
+
const host = new AutomergeHost({
|
|
27
|
+
db: level.sublevel('automerge'),
|
|
28
|
+
});
|
|
29
|
+
await host.open();
|
|
25
30
|
afterTest(() => host.close());
|
|
31
|
+
|
|
26
32
|
const dataService = new DataServiceImpl(host);
|
|
27
33
|
const client = new AutomergeContext(dataService);
|
|
28
34
|
afterTest(() => client.close());
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { MemorySignalManagerContext } from '@dxos/messaging';
|
|
6
6
|
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
7
|
-
import { describe, test } from '@dxos/test';
|
|
7
|
+
import { describe, openAndClose, test } from '@dxos/test';
|
|
8
8
|
|
|
9
9
|
import { createServiceContext } from '../testing';
|
|
10
10
|
import { performInvitation } from '../testing/invitation-utils';
|
|
@@ -12,10 +12,10 @@ import { performInvitation } from '../testing/invitation-utils';
|
|
|
12
12
|
describe('services/ServiceContext', () => {
|
|
13
13
|
test('new space is synchronized on device invitations', async () => {
|
|
14
14
|
const networkContext = new MemorySignalManagerContext();
|
|
15
|
-
const device1 = createServiceContext({ signalContext: networkContext });
|
|
15
|
+
const device1 = await createServiceContext({ signalContext: networkContext });
|
|
16
16
|
await device1.createIdentity();
|
|
17
17
|
|
|
18
|
-
const device2 = createServiceContext({ signalContext: networkContext });
|
|
18
|
+
const device2 = await createServiceContext({ signalContext: networkContext });
|
|
19
19
|
await Promise.all(performInvitation({ host: device1, guest: device2, options: { kind: Invitation.Kind.DEVICE } }));
|
|
20
20
|
|
|
21
21
|
const space1 = await device1.dataSpaceManager!.createSpace();
|
|
@@ -26,13 +26,16 @@ describe('services/ServiceContext', () => {
|
|
|
26
26
|
|
|
27
27
|
test('joined space is synchronized on device invitations', async () => {
|
|
28
28
|
const networkContext = new MemorySignalManagerContext();
|
|
29
|
-
const device1 = createServiceContext({ signalContext: networkContext });
|
|
29
|
+
const device1 = await createServiceContext({ signalContext: networkContext });
|
|
30
|
+
await openAndClose(device1.automergeHost);
|
|
30
31
|
await device1.createIdentity();
|
|
31
32
|
|
|
32
|
-
const device2 = createServiceContext({ signalContext: networkContext });
|
|
33
|
+
const device2 = await createServiceContext({ signalContext: networkContext });
|
|
34
|
+
await openAndClose(device2.automergeHost);
|
|
33
35
|
await Promise.all(performInvitation({ host: device1, guest: device2, options: { kind: Invitation.Kind.DEVICE } }));
|
|
34
36
|
|
|
35
|
-
const identity2 = createServiceContext({ signalContext: networkContext });
|
|
37
|
+
const identity2 = await createServiceContext({ signalContext: networkContext });
|
|
38
|
+
await openAndClose(identity2.automergeHost);
|
|
36
39
|
await identity2.createIdentity();
|
|
37
40
|
const space1 = await identity2.dataSpaceManager!.createSpace();
|
|
38
41
|
await Promise.all(
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { type Level } from 'level';
|
|
6
|
+
|
|
5
7
|
import { Trigger } from '@dxos/async';
|
|
6
|
-
import { Context } from '@dxos/context';
|
|
8
|
+
import { Context, Resource } from '@dxos/context';
|
|
7
9
|
import { getCredentialAssertion, type CredentialProcessor } from '@dxos/credentials';
|
|
8
10
|
import { failUndefined } from '@dxos/debug';
|
|
9
11
|
import { AutomergeHost, MetadataStore, SnapshotStore, SpaceManager, valueEncoding } from '@dxos/echo-pipeline';
|
|
@@ -30,7 +32,7 @@ import {
|
|
|
30
32
|
type IdentityManagerRuntimeParams,
|
|
31
33
|
type JoinIdentityParams,
|
|
32
34
|
} from '../identity';
|
|
33
|
-
import {
|
|
35
|
+
import { createDocumentsIterator, createSelectedDocumentsIterator } from '../indexing';
|
|
34
36
|
import {
|
|
35
37
|
DeviceInvitationProtocol,
|
|
36
38
|
InvitationsHandler,
|
|
@@ -47,7 +49,7 @@ export type ServiceContextRuntimeParams = IdentityManagerRuntimeParams & DataSpa
|
|
|
47
49
|
// TODO(dmaretskyi): Gets duplicated in CJS build between normal and testing bundles.
|
|
48
50
|
@safeInstanceof('dxos.client-services.ServiceContext')
|
|
49
51
|
@Trace.resource()
|
|
50
|
-
export class ServiceContext {
|
|
52
|
+
export class ServiceContext extends Resource {
|
|
51
53
|
public readonly initialized = new Trigger();
|
|
52
54
|
public readonly metadataStore: MetadataStore;
|
|
53
55
|
/**
|
|
@@ -78,10 +80,13 @@ export class ServiceContext {
|
|
|
78
80
|
|
|
79
81
|
constructor(
|
|
80
82
|
public readonly storage: Storage,
|
|
83
|
+
public readonly level: Level<string, string>,
|
|
81
84
|
public readonly networkManager: NetworkManager,
|
|
82
85
|
public readonly signalManager: SignalManager,
|
|
83
86
|
public readonly _runtimeParams?: IdentityManagerRuntimeParams & DataSpaceManagerRuntimeParams,
|
|
84
87
|
) {
|
|
88
|
+
super();
|
|
89
|
+
|
|
85
90
|
// TODO(burdon): Move strings to constants.
|
|
86
91
|
this.metadataStore = new MetadataStore(storage.createDirectory('metadata'));
|
|
87
92
|
this.snapshotStore = new SnapshotStore(storage.createDirectory('snapshots'));
|
|
@@ -115,18 +120,19 @@ export class ServiceContext {
|
|
|
115
120
|
this._runtimeParams as IdentityManagerRuntimeParams,
|
|
116
121
|
);
|
|
117
122
|
|
|
118
|
-
this.indexMetadata = new IndexMetadataStore({
|
|
123
|
+
this.indexMetadata = new IndexMetadataStore({ db: level.sublevel('index-metadata') });
|
|
119
124
|
|
|
120
125
|
this.automergeHost = new AutomergeHost({
|
|
121
126
|
directory: storage.createDirectory('automerge'),
|
|
127
|
+
db: level.sublevel('automerge'),
|
|
122
128
|
metadata: this.indexMetadata,
|
|
123
129
|
});
|
|
124
130
|
|
|
125
131
|
this.indexer = new Indexer({
|
|
126
132
|
indexStore: new IndexStore({ directory: storage.createDirectory('index-store') }),
|
|
127
133
|
metadataStore: this.indexMetadata,
|
|
128
|
-
loadDocuments:
|
|
129
|
-
getAllDocuments:
|
|
134
|
+
loadDocuments: createSelectedDocumentsIterator(this.automergeHost),
|
|
135
|
+
getAllDocuments: createDocumentsIterator(this.automergeHost),
|
|
130
136
|
});
|
|
131
137
|
|
|
132
138
|
this.invitations = new InvitationsHandler(this.networkManager);
|
|
@@ -145,7 +151,7 @@ export class ServiceContext {
|
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
@Trace.span()
|
|
148
|
-
async
|
|
154
|
+
protected override async _open(ctx: Context) {
|
|
149
155
|
await this._checkStorageVersion();
|
|
150
156
|
|
|
151
157
|
log('opening...');
|
|
@@ -153,6 +159,7 @@ export class ServiceContext {
|
|
|
153
159
|
await this.signalManager.open();
|
|
154
160
|
await this.networkManager.open();
|
|
155
161
|
|
|
162
|
+
await this.automergeHost.open();
|
|
156
163
|
await this.metadataStore.load();
|
|
157
164
|
await this.spaceManager.open();
|
|
158
165
|
await this.identityManager.open(ctx);
|
|
@@ -163,7 +170,7 @@ export class ServiceContext {
|
|
|
163
170
|
log('opened');
|
|
164
171
|
}
|
|
165
172
|
|
|
166
|
-
async
|
|
173
|
+
protected override async _close() {
|
|
167
174
|
log('closing...');
|
|
168
175
|
if (this._deviceSpaceSync && this.identityManager.identity) {
|
|
169
176
|
await this.identityManager.identity.space.spaceState.removeCredentialProcessor(this._deviceSpaceSync);
|
|
@@ -2,12 +2,20 @@
|
|
|
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
|
+
import {
|
|
12
|
+
DataServiceImpl,
|
|
13
|
+
type ObjectStructure,
|
|
14
|
+
encodeReference,
|
|
15
|
+
type SpaceDoc,
|
|
16
|
+
type MyLevel,
|
|
17
|
+
} from '@dxos/echo-pipeline';
|
|
18
|
+
import * as E from '@dxos/echo-schema';
|
|
11
19
|
import { IndexServiceImpl } from '@dxos/indexing';
|
|
12
20
|
import { invariant } from '@dxos/invariant';
|
|
13
21
|
import { PublicKey } from '@dxos/keys';
|
|
@@ -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?: MyLevel;
|
|
47
60
|
lockKey?: string;
|
|
48
61
|
callbacks?: ClientServicesHostCallbacks;
|
|
49
62
|
runtimeParams?: ServiceContextRuntimeParams;
|
|
@@ -76,11 +89,13 @@ export class ClientServicesHost {
|
|
|
76
89
|
private _signalManager?: SignalManager;
|
|
77
90
|
private _networkManager?: NetworkManager;
|
|
78
91
|
private _storage?: Storage;
|
|
92
|
+
private _level?: Level<string, string>;
|
|
79
93
|
private _callbacks?: ClientServicesHostCallbacks;
|
|
80
94
|
private _devtoolsProxy?: WebsocketRpcClient<{}, ClientServices>;
|
|
81
95
|
|
|
82
96
|
private _serviceContext!: ServiceContext;
|
|
83
97
|
private readonly _runtimeParams?: ServiceContextRuntimeParams;
|
|
98
|
+
private diagnosticsBroadcastHandler: CollectDiagnosticsBroadcastHandler;
|
|
84
99
|
|
|
85
100
|
@Trace.info()
|
|
86
101
|
private _opening = false;
|
|
@@ -93,12 +108,14 @@ export class ClientServicesHost {
|
|
|
93
108
|
transportFactory,
|
|
94
109
|
signalManager,
|
|
95
110
|
storage,
|
|
111
|
+
level,
|
|
96
112
|
// TODO(wittjosiah): Turn this on by default.
|
|
97
113
|
lockKey,
|
|
98
114
|
callbacks,
|
|
99
115
|
runtimeParams,
|
|
100
116
|
}: ClientServicesHostParams = {}) {
|
|
101
117
|
this._storage = storage;
|
|
118
|
+
this._level = level;
|
|
102
119
|
this._callbacks = callbacks;
|
|
103
120
|
this._runtimeParams = runtimeParams;
|
|
104
121
|
|
|
@@ -138,6 +155,7 @@ export class ClientServicesHost {
|
|
|
138
155
|
},
|
|
139
156
|
});
|
|
140
157
|
|
|
158
|
+
this.diagnosticsBroadcastHandler = createCollectDiagnosticsBroadcastHandler(this._systemService);
|
|
141
159
|
this._loggingService = new LoggingServiceImpl();
|
|
142
160
|
|
|
143
161
|
this._serviceRegistry = new ServiceRegistry<ClientServices>(clientServiceBundle, {
|
|
@@ -226,12 +244,19 @@ export class ClientServicesHost {
|
|
|
226
244
|
|
|
227
245
|
this._opening = true;
|
|
228
246
|
log('opening...', { lockKey: this._resourceLock?.lockKey });
|
|
247
|
+
|
|
248
|
+
if (!this._level) {
|
|
249
|
+
this._level = await createLevel(this._config.get('runtime.client.storage', {})!);
|
|
250
|
+
}
|
|
251
|
+
await this._level.open();
|
|
252
|
+
|
|
229
253
|
await this._resourceLock?.acquire();
|
|
230
254
|
|
|
231
255
|
await this._loggingService.open();
|
|
232
256
|
|
|
233
257
|
this._serviceContext = new ServiceContext(
|
|
234
258
|
this._storage,
|
|
259
|
+
this._level,
|
|
235
260
|
this._networkManager,
|
|
236
261
|
this._signalManager,
|
|
237
262
|
this._runtimeParams,
|
|
@@ -301,6 +326,7 @@ export class ClientServicesHost {
|
|
|
301
326
|
});
|
|
302
327
|
void this._devtoolsProxy.open();
|
|
303
328
|
}
|
|
329
|
+
this.diagnosticsBroadcastHandler.start();
|
|
304
330
|
|
|
305
331
|
this._opening = false;
|
|
306
332
|
this._open = true;
|
|
@@ -319,10 +345,12 @@ export class ClientServicesHost {
|
|
|
319
345
|
|
|
320
346
|
const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
|
|
321
347
|
log('closing...', { deviceKey });
|
|
348
|
+
this.diagnosticsBroadcastHandler.stop();
|
|
322
349
|
await this._devtoolsProxy?.close();
|
|
323
350
|
this._serviceRegistry.setServices({ SystemService: this._systemService });
|
|
324
351
|
await this._loggingService.close();
|
|
325
352
|
await this._serviceContext.close();
|
|
353
|
+
await this._level?.close();
|
|
326
354
|
this._open = false;
|
|
327
355
|
this._statusUpdate.emit();
|
|
328
356
|
log('closed', { deviceKey });
|
|
@@ -347,18 +375,30 @@ export class ClientServicesHost {
|
|
|
347
375
|
await this._serviceContext.initialized.wait();
|
|
348
376
|
const space = await this._serviceContext.dataSpaceManager!.createSpace();
|
|
349
377
|
|
|
350
|
-
const obj: TypedObject = new Properties(undefined);
|
|
351
|
-
obj[defaultKey] = identity.identityKey.toHex();
|
|
352
|
-
|
|
353
378
|
const automergeIndex = space.automergeSpaceState.rootUrl;
|
|
354
379
|
invariant(automergeIndex);
|
|
355
380
|
const document = await this._serviceContext.automergeHost.repo.find<SpaceDoc>(automergeIndex as any);
|
|
356
381
|
await document.whenReady();
|
|
357
382
|
|
|
383
|
+
// TODO(dmaretskyi): Better API for low-level data access.
|
|
384
|
+
const properties: ObjectStructure = {
|
|
385
|
+
system: {
|
|
386
|
+
type: encodeReference(E.getTypeReference(Properties)!),
|
|
387
|
+
},
|
|
388
|
+
data: {
|
|
389
|
+
[defaultKey]: identity.identityKey.toHex(),
|
|
390
|
+
},
|
|
391
|
+
meta: {
|
|
392
|
+
keys: [],
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
const propertiesId = PublicKey.random().toHex();
|
|
358
396
|
document.change((doc: SpaceDoc) => {
|
|
359
|
-
assignDeep(doc, ['objects',
|
|
397
|
+
assignDeep(doc, ['objects', propertiesId], properties);
|
|
360
398
|
});
|
|
361
399
|
|
|
400
|
+
await this._serviceContext.automergeHost.repo.flush();
|
|
401
|
+
|
|
362
402
|
return identity;
|
|
363
403
|
}
|
|
364
404
|
}
|
|
@@ -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);
|
|
@@ -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();
|