@dxos/client-services 0.1.56-main.09ca29d → 0.1.56-main.0e9f69e
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-FD4L7N4K.mjs → chunk-II6TDINB.mjs} +782 -511
- package/dist/lib/browser/chunk-II6TDINB.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +30 -31
- 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 +31 -46
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +918 -649
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +1198 -939
- package/dist/lib/node/packlets/testing/index.cjs.map +4 -4
- package/dist/types/src/packlets/identity/identity-manager.d.ts +7 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-service.d.ts +7 -3
- package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +3 -2
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/logging/logging-service.d.ts +6 -2
- package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
- package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -1
- package/dist/types/src/packlets/services/diagnostics.d.ts +46 -0
- package/dist/types/src/packlets/services/diagnostics.d.ts.map +1 -0
- package/dist/types/src/packlets/services/index.d.ts +1 -0
- package/dist/types/src/packlets/services/index.d.ts.map +1 -1
- package/dist/types/src/packlets/services/platform.d.ts +16 -0
- package/dist/types/src/packlets/services/platform.d.ts.map +1 -0
- package/dist/types/src/packlets/services/service-context.d.ts +2 -1
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +7 -3
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/services/util.d.ts +6 -0
- package/dist/types/src/packlets/services/util.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/system/system-service.d.ts +12 -2
- package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-session.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +2 -0
- package/dist/types/src/version.d.ts.map +1 -0
- package/package.json +34 -33
- package/src/packlets/devices/devices-service.test.ts +2 -1
- package/src/packlets/identity/identity-manager.test.ts +15 -3
- package/src/packlets/identity/identity-manager.ts +27 -6
- package/src/packlets/identity/identity-service.test.ts +17 -2
- package/src/packlets/identity/identity-service.ts +23 -14
- package/src/packlets/identity/identity.test.ts +7 -6
- package/src/packlets/identity/identity.ts +8 -3
- package/src/packlets/invitations/device-invitation-protocol.test.ts +2 -1
- package/src/packlets/logging/logging-service.ts +17 -9
- package/src/packlets/network/network-service.test.ts +2 -1
- package/src/packlets/services/client-rpc-server.ts +4 -4
- package/src/packlets/services/diagnostics.ts +211 -0
- package/src/packlets/services/index.ts +1 -0
- package/src/packlets/services/platform.ts +48 -0
- package/src/packlets/services/service-context.test.ts +59 -0
- package/src/packlets/services/service-context.ts +16 -9
- package/src/packlets/services/service-host.test.ts +11 -10
- package/src/packlets/services/service-host.ts +38 -18
- package/src/packlets/services/service-registry.test.ts +3 -1
- package/src/packlets/services/util.ts +33 -0
- package/src/packlets/spaces/data-space-manager.test.ts +80 -22
- package/src/packlets/spaces/data-space.ts +17 -5
- package/src/packlets/spaces/spaces-service.test.ts +2 -1
- package/src/packlets/system/system-service.test.ts +1 -0
- package/src/packlets/system/system-service.ts +34 -2
- package/src/packlets/testing/test-builder.ts +1 -1
- package/src/packlets/vault/iframe-host-runtime.ts +2 -1
- package/src/packlets/vault/worker-runtime.ts +3 -2
- package/src/packlets/vault/worker-session.ts +6 -1
- package/src/version.ts +1 -0
- package/dist/lib/browser/chunk-FD4L7N4K.mjs.map +0 -7
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Event } from '@dxos/async';
|
|
6
|
+
import { Context } from '@dxos/context';
|
|
6
7
|
import { createCredentialSignerWithKey, CredentialGenerator } from '@dxos/credentials';
|
|
7
8
|
import { MetadataStore, SpaceManager, SwarmIdentity } from '@dxos/echo-pipeline';
|
|
8
9
|
import { FeedStore } from '@dxos/feed-store';
|
|
@@ -13,8 +14,9 @@ import { log } from '@dxos/log';
|
|
|
13
14
|
import { trace } from '@dxos/protocols';
|
|
14
15
|
import { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
15
16
|
import { IdentityRecord, SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
16
|
-
import { AdmittedFeed } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
17
|
+
import { AdmittedFeed, ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
17
18
|
import { Timeframe } from '@dxos/timeframe';
|
|
19
|
+
import { trace as Trace } from '@dxos/tracing';
|
|
18
20
|
import { deferFunction } from '@dxos/util';
|
|
19
21
|
|
|
20
22
|
import { createAuthProvider } from './authenticator';
|
|
@@ -46,6 +48,7 @@ export type CreateIdentityOptions = {
|
|
|
46
48
|
};
|
|
47
49
|
|
|
48
50
|
// TODO(dmaretskyi): Rename: represents the peer's state machine.
|
|
51
|
+
@Trace.resource()
|
|
49
52
|
export class IdentityManager {
|
|
50
53
|
readonly stateUpdate = new Event();
|
|
51
54
|
|
|
@@ -64,7 +67,8 @@ export class IdentityManager {
|
|
|
64
67
|
return this._identity;
|
|
65
68
|
}
|
|
66
69
|
|
|
67
|
-
|
|
70
|
+
@Trace.span()
|
|
71
|
+
async open(ctx: Context) {
|
|
68
72
|
const traceId = PublicKey.random().toHex();
|
|
69
73
|
log.trace('dxos.halo.identity-manager.open', trace.begin({ id: traceId }));
|
|
70
74
|
await this._metadataStore.load();
|
|
@@ -73,7 +77,7 @@ export class IdentityManager {
|
|
|
73
77
|
log('identity record', { identityRecord });
|
|
74
78
|
if (identityRecord) {
|
|
75
79
|
this._identity = await this._constructIdentity(identityRecord);
|
|
76
|
-
await this._identity.open();
|
|
80
|
+
await this._identity.open(ctx);
|
|
77
81
|
await this._identity.ready();
|
|
78
82
|
log.trace('dxos.halo.identity', {
|
|
79
83
|
identityKey: identityRecord.identityKey,
|
|
@@ -85,7 +89,7 @@ export class IdentityManager {
|
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
async close() {
|
|
88
|
-
await this._identity?.close();
|
|
92
|
+
await this._identity?.close(new Context());
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
async createIdentity({ displayName }: CreateIdentityOptions = {}) {
|
|
@@ -105,7 +109,7 @@ export class IdentityManager {
|
|
|
105
109
|
};
|
|
106
110
|
|
|
107
111
|
const identity = await this._constructIdentity(identityRecord);
|
|
108
|
-
await identity.open();
|
|
112
|
+
await identity.open(new Context());
|
|
109
113
|
|
|
110
114
|
{
|
|
111
115
|
const generator = new CredentialGenerator(this._keyring, identityRecord.identityKey, identityRecord.deviceKey);
|
|
@@ -175,7 +179,7 @@ export class IdentityManager {
|
|
|
175
179
|
};
|
|
176
180
|
const identity = await this._constructIdentity(identityRecord);
|
|
177
181
|
|
|
178
|
-
await identity.open();
|
|
182
|
+
await identity.open(new Context());
|
|
179
183
|
this._identity = identity;
|
|
180
184
|
await this._metadataStore.setIdentityRecord(identityRecord);
|
|
181
185
|
await this._identity.ready();
|
|
@@ -189,6 +193,23 @@ export class IdentityManager {
|
|
|
189
193
|
return identity;
|
|
190
194
|
}
|
|
191
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Update the profile document of an existing identity.
|
|
198
|
+
*/
|
|
199
|
+
async updateProfile(profile: ProfileDocument) {
|
|
200
|
+
invariant(this._identity, 'Identity not initialized.');
|
|
201
|
+
// TODO(wittjosiah): Use CredentialGenerator.
|
|
202
|
+
const credential = await this._identity.getIdentityCredentialSigner().createCredential({
|
|
203
|
+
subject: this._identity.identityKey,
|
|
204
|
+
assertion: {
|
|
205
|
+
'@type': 'dxos.halo.credentials.IdentityProfile',
|
|
206
|
+
profile,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
const receipt = await this._identity.controlPipeline.writer.write({ credential: { credential } });
|
|
210
|
+
await this._identity.controlPipeline.state.waitUntilTimeframe(new Timeframe([[receipt.feedKey, receipt.seq]]));
|
|
211
|
+
}
|
|
212
|
+
|
|
192
213
|
private async _constructIdentity(identityRecord: IdentityRecord) {
|
|
193
214
|
invariant(!this._identity);
|
|
194
215
|
log('constructing identity', { identityRecord });
|
|
@@ -6,6 +6,7 @@ import chai, { expect } from 'chai';
|
|
|
6
6
|
import chaiAsPromised from 'chai-as-promised';
|
|
7
7
|
|
|
8
8
|
import { Trigger } from '@dxos/async';
|
|
9
|
+
import { Context } from '@dxos/context';
|
|
9
10
|
import { PublicKey } from '@dxos/keys';
|
|
10
11
|
import { Identity, IdentityService } from '@dxos/protocols/proto/dxos/client/services';
|
|
11
12
|
import { afterEach, afterTest, beforeEach, describe, test } from '@dxos/test';
|
|
@@ -22,8 +23,12 @@ describe('IdentityService', () => {
|
|
|
22
23
|
|
|
23
24
|
beforeEach(async () => {
|
|
24
25
|
serviceContext = createServiceContext();
|
|
25
|
-
await serviceContext.open();
|
|
26
|
-
identityService = new IdentityServiceImpl(
|
|
26
|
+
await serviceContext.open(new Context());
|
|
27
|
+
identityService = new IdentityServiceImpl(
|
|
28
|
+
(options) => serviceContext.createIdentity(options),
|
|
29
|
+
serviceContext.identityManager,
|
|
30
|
+
serviceContext.keyring,
|
|
31
|
+
);
|
|
27
32
|
});
|
|
28
33
|
|
|
29
34
|
afterEach(async () => {
|
|
@@ -54,6 +59,16 @@ describe('IdentityService', () => {
|
|
|
54
59
|
|
|
55
60
|
describe.skip('recoverIdentity', () => {});
|
|
56
61
|
|
|
62
|
+
describe('updateProfile', () => {
|
|
63
|
+
test('updates profile', async () => {
|
|
64
|
+
const identity = await identityService.createIdentity({});
|
|
65
|
+
expect(identity.profile?.displayName).to.be.undefined;
|
|
66
|
+
|
|
67
|
+
const updatedIdentity = await identityService.updateProfile({ displayName: 'Example' });
|
|
68
|
+
expect(updatedIdentity.profile?.displayName).to.equal('Example');
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
57
72
|
describe('queryIdentity', () => {
|
|
58
73
|
test('returns undefined if no identity is available', async () => {
|
|
59
74
|
const query = identityService.queryIdentity();
|
|
@@ -6,6 +6,7 @@ import { Stream } from '@dxos/codec-protobuf';
|
|
|
6
6
|
import { signPresentation } from '@dxos/credentials';
|
|
7
7
|
import { todo } from '@dxos/debug';
|
|
8
8
|
import { invariant } from '@dxos/invariant';
|
|
9
|
+
import { Keyring } from '@dxos/keyring';
|
|
9
10
|
import {
|
|
10
11
|
Identity,
|
|
11
12
|
IdentityService,
|
|
@@ -15,15 +16,17 @@ import {
|
|
|
15
16
|
} from '@dxos/protocols/proto/dxos/client/services';
|
|
16
17
|
import { Presentation, ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
17
18
|
|
|
18
|
-
import {
|
|
19
|
+
import { CreateIdentityOptions, IdentityManager } from './identity-manager';
|
|
19
20
|
|
|
20
21
|
export class IdentityServiceImpl implements IdentityService {
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
constructor(
|
|
23
|
+
private readonly _createIdentity: (params?: CreateIdentityOptions) => Promise<Identity>,
|
|
24
|
+
private readonly _identityManager: IdentityManager,
|
|
25
|
+
private readonly _keyring: Keyring,
|
|
26
|
+
) {}
|
|
23
27
|
|
|
24
28
|
async createIdentity(request: ProfileDocument): Promise<Identity> {
|
|
25
|
-
await this.
|
|
26
|
-
|
|
29
|
+
await this._createIdentity(request);
|
|
27
30
|
return this._getIdentity()!;
|
|
28
31
|
}
|
|
29
32
|
|
|
@@ -36,30 +39,36 @@ export class IdentityServiceImpl implements IdentityService {
|
|
|
36
39
|
const emitNext = () => next({ identity: this._getIdentity() });
|
|
37
40
|
|
|
38
41
|
emitNext();
|
|
39
|
-
return this.
|
|
42
|
+
return this._identityManager.stateUpdate.on(emitNext);
|
|
40
43
|
});
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
private _getIdentity(): Identity | undefined {
|
|
44
|
-
if (!this.
|
|
47
|
+
if (!this._identityManager.identity) {
|
|
45
48
|
return undefined;
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
return {
|
|
49
|
-
identityKey: this.
|
|
50
|
-
spaceKey: this.
|
|
51
|
-
profile: this.
|
|
52
|
+
identityKey: this._identityManager.identity.identityKey,
|
|
53
|
+
spaceKey: this._identityManager.identity.space.key,
|
|
54
|
+
profile: this._identityManager.identity.profileDocument,
|
|
52
55
|
};
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
async updateProfile(profile: ProfileDocument): Promise<Identity> {
|
|
59
|
+
invariant(this._identityManager.identity, 'Identity not initialized.');
|
|
60
|
+
await this._identityManager.updateProfile(profile);
|
|
61
|
+
return this._getIdentity()!;
|
|
62
|
+
}
|
|
63
|
+
|
|
55
64
|
async signPresentation({ presentation, nonce }: SignPresentationRequest): Promise<Presentation> {
|
|
56
|
-
invariant(this.
|
|
65
|
+
invariant(this._identityManager.identity, 'Identity not initialized.');
|
|
57
66
|
|
|
58
67
|
return await signPresentation({
|
|
59
68
|
presentation,
|
|
60
|
-
signer: this.
|
|
61
|
-
signerKey: this.
|
|
62
|
-
chain: this.
|
|
69
|
+
signer: this._keyring,
|
|
70
|
+
signerKey: this._identityManager.identity.deviceKey,
|
|
71
|
+
chain: this._identityManager.identity.deviceCredentialChain,
|
|
63
72
|
nonce,
|
|
64
73
|
});
|
|
65
74
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import expect from 'expect';
|
|
6
6
|
|
|
7
|
+
import { Context } from '@dxos/context';
|
|
7
8
|
import { CredentialGenerator, verifyCredential } from '@dxos/credentials';
|
|
8
9
|
import {
|
|
9
10
|
MetadataStore,
|
|
@@ -106,8 +107,8 @@ describe('identity/identity', () => {
|
|
|
106
107
|
space,
|
|
107
108
|
});
|
|
108
109
|
|
|
109
|
-
await identity.open();
|
|
110
|
-
afterTest(() => identity.close());
|
|
110
|
+
await identity.open(new Context());
|
|
111
|
+
afterTest(() => identity.close(new Context()));
|
|
111
112
|
|
|
112
113
|
//
|
|
113
114
|
// Identity genesis
|
|
@@ -219,8 +220,8 @@ describe('identity/identity', () => {
|
|
|
219
220
|
space,
|
|
220
221
|
}));
|
|
221
222
|
|
|
222
|
-
await identity.open();
|
|
223
|
-
afterTest(() => identity.close());
|
|
223
|
+
await identity.open(new Context());
|
|
224
|
+
afterTest(() => identity.close(new Context()));
|
|
224
225
|
|
|
225
226
|
//
|
|
226
227
|
// Identity genesis
|
|
@@ -310,8 +311,8 @@ describe('identity/identity', () => {
|
|
|
310
311
|
space,
|
|
311
312
|
}));
|
|
312
313
|
|
|
313
|
-
await identity.open();
|
|
314
|
-
afterTest(() => identity.close());
|
|
314
|
+
await identity.open(new Context());
|
|
315
|
+
afterTest(() => identity.close(new Context()));
|
|
315
316
|
}
|
|
316
317
|
|
|
317
318
|
//
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { Event } from '@dxos/async';
|
|
6
6
|
import { AUTH_TIMEOUT, LOAD_CONTROL_FEEDS_TIMEOUT } from '@dxos/client-protocol';
|
|
7
|
+
import { Context } from '@dxos/context';
|
|
7
8
|
import {
|
|
8
9
|
DeviceStateMachine,
|
|
9
10
|
CredentialSigner,
|
|
@@ -21,6 +22,7 @@ import { log } from '@dxos/log';
|
|
|
21
22
|
import { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
22
23
|
import { AdmittedFeed, ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
23
24
|
import { DeviceAdmissionRequest } from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
25
|
+
import { trace } from '@dxos/tracing';
|
|
24
26
|
import { ComplexSet } from '@dxos/util';
|
|
25
27
|
|
|
26
28
|
import { TrustedKeySetAuthVerifier } from './authenticator';
|
|
@@ -35,6 +37,7 @@ export type IdentityParams = {
|
|
|
35
37
|
/**
|
|
36
38
|
* Agent identity manager, which includes the agent's Halo space.
|
|
37
39
|
*/
|
|
40
|
+
@trace.resource()
|
|
38
41
|
export class Identity {
|
|
39
42
|
public readonly space: Space;
|
|
40
43
|
private readonly _signer: Signer;
|
|
@@ -76,13 +79,15 @@ export class Identity {
|
|
|
76
79
|
return this._deviceStateMachine.authorizedDeviceKeys;
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
|
|
82
|
+
@trace.span()
|
|
83
|
+
async open(ctx: Context) {
|
|
80
84
|
await this.space.spaceState.addCredentialProcessor(this._deviceStateMachine);
|
|
81
85
|
await this.space.spaceState.addCredentialProcessor(this._profileStateMachine);
|
|
82
|
-
await this.space.open();
|
|
86
|
+
await this.space.open(ctx);
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
|
|
89
|
+
@trace.span()
|
|
90
|
+
async close(ctx: Context) {
|
|
86
91
|
await this.authVerifier.close();
|
|
87
92
|
await this.space.spaceState.removeCredentialProcessor(this._profileStateMachine);
|
|
88
93
|
await this.space.spaceState.removeCredentialProcessor(this._deviceStateMachine);
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
6
|
|
|
7
7
|
import { asyncChain } from '@dxos/async';
|
|
8
|
+
import { Context } from '@dxos/context';
|
|
8
9
|
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
9
10
|
import { describe, test, afterTest } from '@dxos/test';
|
|
10
11
|
|
|
@@ -19,7 +20,7 @@ const closeAfterTest = async (peer: ServiceContext) => {
|
|
|
19
20
|
describe('services/device', () => {
|
|
20
21
|
test('creates identity', async () => {
|
|
21
22
|
const peer = createServiceContext();
|
|
22
|
-
await peer.open();
|
|
23
|
+
await peer.open(new Context());
|
|
23
24
|
afterTest(() => peer.close());
|
|
24
25
|
|
|
25
26
|
const identity = await peer.createIdentity();
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
ControlMetricsRequest,
|
|
14
14
|
ControlMetricsResponse,
|
|
15
15
|
QueryMetricsRequest,
|
|
16
|
+
QueryMetricsResponse,
|
|
16
17
|
} from '@dxos/protocols/proto/dxos/client/services';
|
|
17
18
|
import { jsonify, numericalValues, tracer } from '@dxos/util';
|
|
18
19
|
|
|
@@ -21,6 +22,7 @@ import { jsonify, numericalValues, tracer } from '@dxos/util';
|
|
|
21
22
|
*/
|
|
22
23
|
export class LoggingServiceImpl implements LoggingService {
|
|
23
24
|
private readonly _logs = new Event<NaturalLogEntry>();
|
|
25
|
+
private readonly _started = new Date();
|
|
24
26
|
|
|
25
27
|
async open() {
|
|
26
28
|
log.runtimeConfig.processors.push(this._logProcessor);
|
|
@@ -45,11 +47,14 @@ export class LoggingServiceImpl implements LoggingService {
|
|
|
45
47
|
return { recording: tracer.recording };
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
/**
|
|
51
|
+
* @deprecated (Move to diagnostics).
|
|
52
|
+
*/
|
|
53
|
+
queryMetrics({ interval = 5_000 }: QueryMetricsRequest): Stream<QueryMetricsResponse> {
|
|
49
54
|
// TODO(burdon): Map all traces; how to bind to reducer/metrics shape (e.g., numericalValues)?
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
return { key, stats: numericalValues(
|
|
55
|
+
const getNumericalValues = (key: string) => {
|
|
56
|
+
const events = tracer.get(key) ?? [];
|
|
57
|
+
return { key, stats: numericalValues(events, 'duration') };
|
|
53
58
|
};
|
|
54
59
|
|
|
55
60
|
return new Stream(({ next }) => {
|
|
@@ -57,16 +62,19 @@ export class LoggingServiceImpl implements LoggingService {
|
|
|
57
62
|
const metrics: Metrics = {
|
|
58
63
|
timestamp: new Date(),
|
|
59
64
|
values: [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
],
|
|
65
|
+
getNumericalValues('dxos.echo.pipeline.control'),
|
|
66
|
+
getNumericalValues('dxos.echo.pipeline.data'),
|
|
67
|
+
].filter(Boolean) as Metrics.KeyPair[],
|
|
63
68
|
};
|
|
64
69
|
|
|
65
|
-
next(
|
|
70
|
+
next({
|
|
71
|
+
timestamp: new Date(),
|
|
72
|
+
metrics,
|
|
73
|
+
});
|
|
66
74
|
};
|
|
67
75
|
|
|
68
76
|
update();
|
|
69
|
-
const i = setInterval(update, interval);
|
|
77
|
+
const i = setInterval(update, Math.max(interval, 1_000));
|
|
70
78
|
return () => {
|
|
71
79
|
clearInterval(i);
|
|
72
80
|
};
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
6
|
|
|
7
7
|
import { Trigger } from '@dxos/async';
|
|
8
|
+
import { Context } from '@dxos/context';
|
|
8
9
|
import { NetworkService, ConnectionState } from '@dxos/protocols/proto/dxos/client/services';
|
|
9
10
|
import { afterEach, afterTest, beforeEach, describe, test } from '@dxos/test';
|
|
10
11
|
|
|
@@ -18,7 +19,7 @@ describe('NetworkService', () => {
|
|
|
18
19
|
|
|
19
20
|
beforeEach(async () => {
|
|
20
21
|
serviceContext = createServiceContext();
|
|
21
|
-
await serviceContext.open();
|
|
22
|
+
await serviceContext.open(new Context());
|
|
22
23
|
networkService = new NetworkServiceImpl(serviceContext.networkManager, serviceContext.signalManager);
|
|
23
24
|
});
|
|
24
25
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { ClientServices } from '@dxos/client-protocol';
|
|
6
6
|
import { Any, ServiceHandler, Stream } from '@dxos/codec-protobuf';
|
|
7
7
|
import { raise } from '@dxos/debug';
|
|
8
|
-
import { parseMethodName, RpcPeer, RpcPeerOptions } from '@dxos/rpc';
|
|
8
|
+
import { parseMethodName, RpcPeer, RpcPeerOptions, ServiceBundle } from '@dxos/rpc';
|
|
9
9
|
import { MaybePromise } from '@dxos/util';
|
|
10
10
|
|
|
11
11
|
import { ServiceRegistry } from './service-registry';
|
|
@@ -74,16 +74,16 @@ export class ClientRpcServer {
|
|
|
74
74
|
private _getServiceHandler(serviceName: string) {
|
|
75
75
|
if (!this._handlerCache.has(serviceName)) {
|
|
76
76
|
const [key, descriptor] =
|
|
77
|
-
Object.entries(this._serviceRegistry.descriptors).find(
|
|
77
|
+
Object.entries(this._serviceRegistry.descriptors as ServiceBundle<Record<string, any>>).find(
|
|
78
78
|
([key, descriptor]) => descriptor.name === serviceName,
|
|
79
79
|
) ?? raise(new Error(`Service not available: ${serviceName}`));
|
|
80
80
|
|
|
81
|
-
const service = this._serviceRegistry.services[key as keyof ClientServices];
|
|
81
|
+
const service = this._serviceRegistry.services[key as keyof ClientServices] as any;
|
|
82
82
|
if (!service) {
|
|
83
83
|
throw new Error(`Service not available: ${serviceName}`);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
this._handlerCache.set(serviceName, descriptor.createServer(service
|
|
86
|
+
this._handlerCache.set(serviceName, descriptor.createServer(service));
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
return this._handlerCache.get(serviceName)!;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { ClientServices } from '@dxos/client-protocol';
|
|
6
|
+
import { getFirstStreamValue } from '@dxos/codec-protobuf';
|
|
7
|
+
import { Config, ConfigProto } from '@dxos/config';
|
|
8
|
+
import { credentialTypeFilter } from '@dxos/credentials';
|
|
9
|
+
import { DocumentModel, DocumentModelState } from '@dxos/document-model';
|
|
10
|
+
import { invariant } from '@dxos/invariant';
|
|
11
|
+
import { PublicKey } from '@dxos/keys';
|
|
12
|
+
import { log } from '@dxos/log';
|
|
13
|
+
import { STORAGE_VERSION } from '@dxos/protocols';
|
|
14
|
+
import {
|
|
15
|
+
Device,
|
|
16
|
+
Identity,
|
|
17
|
+
Metrics,
|
|
18
|
+
Space as SpaceProto,
|
|
19
|
+
SpaceMember,
|
|
20
|
+
} from '@dxos/protocols/proto/dxos/client/services';
|
|
21
|
+
import { SubscribeToFeedsResponse } from '@dxos/protocols/proto/dxos/devtools/host';
|
|
22
|
+
import { Epoch } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
23
|
+
|
|
24
|
+
import { DXOS_VERSION } from '../../version';
|
|
25
|
+
import { DataSpace } from '../spaces';
|
|
26
|
+
import { getPlatform, Platform } from './platform';
|
|
27
|
+
import { ServiceContext } from './service-context';
|
|
28
|
+
|
|
29
|
+
const DEFAULT_TIMEOUT = 1_000;
|
|
30
|
+
|
|
31
|
+
export type Diagnostics = {
|
|
32
|
+
created: string;
|
|
33
|
+
platform: Platform;
|
|
34
|
+
config?: ConfigProto;
|
|
35
|
+
client: {
|
|
36
|
+
version: string;
|
|
37
|
+
storage: {
|
|
38
|
+
version: number;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
identity?: Identity;
|
|
42
|
+
devices?: Device[];
|
|
43
|
+
spaces?: SpaceStats[];
|
|
44
|
+
feeds?: Partial<SubscribeToFeedsResponse.Feed>[];
|
|
45
|
+
metrics?: Metrics;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// TODO(burdon): Normalize for ECHO/HALO.
|
|
49
|
+
export type SpaceStats = {
|
|
50
|
+
key: PublicKey;
|
|
51
|
+
properties?: {
|
|
52
|
+
name: string;
|
|
53
|
+
};
|
|
54
|
+
db?: {
|
|
55
|
+
objects: number;
|
|
56
|
+
};
|
|
57
|
+
metrics?: SpaceProto.Metrics & {
|
|
58
|
+
startupTime?: number;
|
|
59
|
+
};
|
|
60
|
+
epochs?: (Epoch & { id?: PublicKey })[];
|
|
61
|
+
members?: SpaceMember[];
|
|
62
|
+
pipeline?: SpaceProto.PipelineState;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Create diagnostics to provide snapshot of current system state.
|
|
67
|
+
*/
|
|
68
|
+
export const createDiagnostics = async (
|
|
69
|
+
clientServices: Partial<ClientServices>,
|
|
70
|
+
serviceContext: ServiceContext,
|
|
71
|
+
config: Config,
|
|
72
|
+
): Promise<Diagnostics> => {
|
|
73
|
+
const diagnostics: Diagnostics = {
|
|
74
|
+
created: new Date().toISOString(),
|
|
75
|
+
platform: getPlatform(),
|
|
76
|
+
client: {
|
|
77
|
+
version: DXOS_VERSION,
|
|
78
|
+
storage: {
|
|
79
|
+
version: STORAGE_VERSION,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Trace metrics.
|
|
85
|
+
// TODO(burdon): Move here from logging service?
|
|
86
|
+
{
|
|
87
|
+
invariant(clientServices.LoggingService, 'SystemService is not available.');
|
|
88
|
+
diagnostics.metrics = await getFirstStreamValue(clientServices.LoggingService.queryMetrics({}), {
|
|
89
|
+
timeout: DEFAULT_TIMEOUT,
|
|
90
|
+
}).catch(() => undefined);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const identity = serviceContext.identityManager.identity;
|
|
94
|
+
if (identity) {
|
|
95
|
+
// Identity.
|
|
96
|
+
diagnostics.identity = {
|
|
97
|
+
identityKey: identity.identityKey,
|
|
98
|
+
spaceKey: identity.space.key,
|
|
99
|
+
profile: identity.profileDocument,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Devices.
|
|
103
|
+
const { devices } =
|
|
104
|
+
(await getFirstStreamValue(clientServices.DevicesService!.queryDevices(), {
|
|
105
|
+
timeout: DEFAULT_TIMEOUT,
|
|
106
|
+
}).catch(() => undefined)) ?? {};
|
|
107
|
+
diagnostics.devices = devices;
|
|
108
|
+
|
|
109
|
+
// TODO(dmaretskyi): Add metrics for halo space.
|
|
110
|
+
|
|
111
|
+
// Spaces.
|
|
112
|
+
if (serviceContext.dataSpaceManager) {
|
|
113
|
+
diagnostics.spaces = await Promise.all(
|
|
114
|
+
Array.from(serviceContext.dataSpaceManager.spaces.values()).map((space) => getSpaceStats(space)) ?? [],
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Feeds.
|
|
119
|
+
const { feeds = [] } =
|
|
120
|
+
(await getFirstStreamValue(clientServices.DevtoolsHost!.subscribeToFeeds({}), {
|
|
121
|
+
timeout: DEFAULT_TIMEOUT,
|
|
122
|
+
}).catch(() => undefined)) ?? {};
|
|
123
|
+
diagnostics.feeds = feeds.map(({ feedKey, bytes, length }) => ({ feedKey, bytes, length }));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
diagnostics.config = config.values;
|
|
127
|
+
|
|
128
|
+
return diagnostics;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const getProperties = (space: DataSpace) => {
|
|
132
|
+
let properties: any = {};
|
|
133
|
+
try {
|
|
134
|
+
// Add properties to cache.
|
|
135
|
+
const propertiesItem = space.dataPipeline.itemManager.items.find(
|
|
136
|
+
(item) =>
|
|
137
|
+
item.modelMeta?.type === DocumentModel.meta.type &&
|
|
138
|
+
(item.state as DocumentModelState)?.type === 'dxos.sdk.client.Properties',
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const state = propertiesItem?.state as DocumentModelState;
|
|
142
|
+
properties = state?.data;
|
|
143
|
+
} catch (err: any) {
|
|
144
|
+
log.warn(err.message);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return properties;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const getSpaceStats = async (space: DataSpace): Promise<SpaceStats> => {
|
|
151
|
+
const stats: SpaceStats = {
|
|
152
|
+
key: space.key,
|
|
153
|
+
metrics: space.metrics,
|
|
154
|
+
|
|
155
|
+
epochs: space.inner.spaceState.credentials
|
|
156
|
+
.filter(credentialTypeFilter('dxos.halo.credentials.Epoch'))
|
|
157
|
+
.map((credential) => ({
|
|
158
|
+
...credential.subject.assertion,
|
|
159
|
+
id: credential.id,
|
|
160
|
+
})),
|
|
161
|
+
|
|
162
|
+
members: Array.from(space.inner.spaceState.members.values()).map((member) => ({
|
|
163
|
+
identity: {
|
|
164
|
+
identityKey: member.key,
|
|
165
|
+
profile: {
|
|
166
|
+
displayName: member.assertion.profile?.displayName,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
presence:
|
|
170
|
+
space.presence.getPeersOnline().filter(({ identityKey }) => identityKey.equals(member.key)).length > 0
|
|
171
|
+
? SpaceMember.PresenceState.ONLINE
|
|
172
|
+
: SpaceMember.PresenceState.OFFLINE,
|
|
173
|
+
})),
|
|
174
|
+
|
|
175
|
+
pipeline: {
|
|
176
|
+
// TODO(burdon): Pick properties from credentials if needed.
|
|
177
|
+
// currentEpoch: space.dataPipeline.currentEpoch,
|
|
178
|
+
// appliedEpoch: space.dataPipeline.appliedEpoch,
|
|
179
|
+
|
|
180
|
+
controlFeeds: space.inner.controlPipeline.state.feeds.map((feed) => feed.key),
|
|
181
|
+
currentControlTimeframe: space.inner.controlPipeline.state.timeframe,
|
|
182
|
+
targetControlTimeframe: space.inner.controlPipeline.state.targetTimeframe,
|
|
183
|
+
totalControlTimeframe: space.inner.controlPipeline.state.endTimeframe,
|
|
184
|
+
|
|
185
|
+
// TODO(burdon): Empty?
|
|
186
|
+
dataFeeds: space.dataPipeline.pipelineState?.feeds.map((feed) => feed.key) ?? [],
|
|
187
|
+
startDataTimeframe: space.dataPipeline.pipelineState?.startTimeframe,
|
|
188
|
+
currentDataTimeframe: space.dataPipeline.pipelineState?.timeframe,
|
|
189
|
+
targetDataTimeframe: space.dataPipeline.pipelineState?.targetTimeframe,
|
|
190
|
+
totalDataTimeframe: space.dataPipeline.pipelineState?.endTimeframe,
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// TODO(burdon): May not be open?
|
|
195
|
+
if (space.dataPipeline.itemManager) {
|
|
196
|
+
Object.assign(stats, {
|
|
197
|
+
properties: getProperties(space),
|
|
198
|
+
db: {
|
|
199
|
+
objects: space.dataPipeline.itemManager.entities.size,
|
|
200
|
+
},
|
|
201
|
+
} as SpaceStats);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// TODO(burdon): Factor out.
|
|
205
|
+
if (stats.metrics) {
|
|
206
|
+
const { open, ready } = stats.metrics;
|
|
207
|
+
stats.metrics.startupTime = open && ready && ready.getTime() - open.getTime();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return stats;
|
|
211
|
+
};
|