@dxos/client-services 0.1.55 → 0.1.56-main.01afb72
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-XSZIBW37.mjs → chunk-53BV3Q7V.mjs} +1131 -463
- package/dist/lib/browser/chunk-53BV3Q7V.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +63 -42
- 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 +13 -4
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +1279 -593
- 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 +1410 -736
- package/dist/lib/node/packlets/testing/index.cjs.map +4 -4
- package/dist/types/src/packlets/identity/identity-manager.d.ts +2 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- 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/invitations/device-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-extension.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.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-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.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/testing/invitation-utils.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/shell-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/authenticator.test.ts +2 -2
- package/src/packlets/identity/identity-manager.test.ts +4 -3
- package/src/packlets/identity/identity-manager.ts +10 -7
- package/src/packlets/identity/identity-service.test.ts +2 -1
- package/src/packlets/identity/identity-service.ts +1 -2
- package/src/packlets/identity/identity.test.ts +7 -6
- package/src/packlets/identity/identity.ts +9 -5
- package/src/packlets/invitations/device-invitation-protocol.test.ts +2 -1
- package/src/packlets/invitations/device-invitation-protocol.ts +1 -2
- package/src/packlets/invitations/invitation-extension.ts +1 -2
- package/src/packlets/invitations/invitations-handler.ts +1 -2
- package/src/packlets/invitations/invitations-service.ts +1 -2
- package/src/packlets/invitations/space-invitation-protocol.ts +1 -2
- package/src/packlets/locks/node.ts +1 -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.ts +14 -11
- package/src/packlets/services/service-host.test.ts +11 -10
- package/src/packlets/services/service-host.ts +38 -23
- 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.ts +2 -3
- package/src/packlets/spaces/data-space.ts +1 -1
- package/src/packlets/spaces/notarization-plugin.ts +1 -2
- package/src/packlets/spaces/spaces-service.test.ts +2 -1
- package/src/packlets/spaces/spaces-service.ts +18 -12
- package/src/packlets/system/system-service.test.ts +1 -0
- package/src/packlets/system/system-service.ts +34 -2
- package/src/packlets/testing/invitation-utils.ts +1 -2
- package/src/packlets/testing/test-builder.ts +1 -1
- package/src/packlets/vault/iframe-host-runtime.ts +2 -1
- package/src/packlets/vault/shell-runtime.ts +1 -2
- package/src/packlets/vault/worker-runtime.ts +2 -1
- package/src/packlets/vault/worker-session.ts +7 -3
- package/src/version.ts +1 -0
- package/dist/lib/browser/chunk-XSZIBW37.mjs.map +0 -7
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
export type Platform = {
|
|
6
|
+
type: 'browser' | 'shared-worker' | 'node';
|
|
7
|
+
userAgent?: string;
|
|
8
|
+
platform?: string;
|
|
9
|
+
runtime?: string;
|
|
10
|
+
uptime?: number;
|
|
11
|
+
memory?: {
|
|
12
|
+
rss: number;
|
|
13
|
+
heapTotal: number;
|
|
14
|
+
heapUsed: number;
|
|
15
|
+
external: number;
|
|
16
|
+
arrayBuffers: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const getPlatform = (): Platform => {
|
|
21
|
+
if ((process as any).browser) {
|
|
22
|
+
if (typeof window !== 'undefined') {
|
|
23
|
+
// Browser.
|
|
24
|
+
const { userAgent } = window.navigator;
|
|
25
|
+
return {
|
|
26
|
+
type: 'browser',
|
|
27
|
+
userAgent,
|
|
28
|
+
uptime: Math.floor((Date.now() - window.performance.timeOrigin) / 1_000),
|
|
29
|
+
};
|
|
30
|
+
} else {
|
|
31
|
+
// Shared worker.
|
|
32
|
+
return {
|
|
33
|
+
type: 'shared-worker',
|
|
34
|
+
uptime: Math.floor((Date.now() - performance.timeOrigin) / 1_000),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
// Node.
|
|
39
|
+
const { platform, version, arch } = process;
|
|
40
|
+
return {
|
|
41
|
+
type: 'node',
|
|
42
|
+
platform: `${platform} ${version} ${arch}`,
|
|
43
|
+
runtime: process.version,
|
|
44
|
+
uptime: Math.floor(process.uptime()),
|
|
45
|
+
memory: process.memoryUsage(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import invariant from 'tiny-invariant';
|
|
6
|
-
|
|
7
5
|
import { Trigger } from '@dxos/async';
|
|
6
|
+
import { Context } from '@dxos/context';
|
|
8
7
|
import { CredentialProcessor, getCredentialAssertion } from '@dxos/credentials';
|
|
9
8
|
import { failUndefined } from '@dxos/debug';
|
|
10
9
|
import {
|
|
@@ -15,6 +14,7 @@ import {
|
|
|
15
14
|
SnapshotStore,
|
|
16
15
|
} from '@dxos/echo-pipeline';
|
|
17
16
|
import { FeedFactory, FeedStore } from '@dxos/feed-store';
|
|
17
|
+
import { invariant } from '@dxos/invariant';
|
|
18
18
|
import { Keyring } from '@dxos/keyring';
|
|
19
19
|
import { PublicKey } from '@dxos/keys';
|
|
20
20
|
import { log } from '@dxos/log';
|
|
@@ -27,6 +27,7 @@ import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
|
27
27
|
import { Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
28
28
|
import { Storage } from '@dxos/random-access-storage';
|
|
29
29
|
import { BlobStore } from '@dxos/teleport-extension-object-sync';
|
|
30
|
+
import { trace as Trace } from '@dxos/tracing';
|
|
30
31
|
|
|
31
32
|
import { CreateIdentityOptions, IdentityManager, JoinIdentityParams } from '../identity';
|
|
32
33
|
import {
|
|
@@ -41,6 +42,7 @@ import { DataSpaceManager, SigningContext } from '../spaces';
|
|
|
41
42
|
* Shared backend for all client services.
|
|
42
43
|
*/
|
|
43
44
|
// TODO(burdon): Rename/break-up into smaller components. And/or make members private.
|
|
45
|
+
@Trace.resource()
|
|
44
46
|
export class ServiceContext {
|
|
45
47
|
public readonly initialized = new Trigger();
|
|
46
48
|
public readonly dataServiceSubscriptions = new DataServiceSubscriptions();
|
|
@@ -122,21 +124,21 @@ export class ServiceContext {
|
|
|
122
124
|
);
|
|
123
125
|
}
|
|
124
126
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
@Trace.span()
|
|
128
|
+
async open(ctx: Context) {
|
|
128
129
|
await this._checkStorageVersion();
|
|
129
130
|
|
|
130
131
|
log('opening...');
|
|
132
|
+
log.trace('dxos.sdk.service-context.open', trace.begin({ id: this._instanceId }));
|
|
131
133
|
await this.signalManager.open();
|
|
132
134
|
await this.networkManager.open();
|
|
133
135
|
await this.spaceManager.open();
|
|
134
|
-
await this.identityManager.open();
|
|
136
|
+
await this.identityManager.open(ctx);
|
|
135
137
|
if (this.identityManager.identity) {
|
|
136
|
-
await this._initialize();
|
|
138
|
+
await this._initialize(ctx);
|
|
137
139
|
}
|
|
138
|
-
log('opened');
|
|
139
140
|
log.trace('dxos.sdk.service-context.open', trace.end({ id: this._instanceId }));
|
|
141
|
+
log('opened');
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
async close() {
|
|
@@ -157,7 +159,7 @@ export class ServiceContext {
|
|
|
157
159
|
async createIdentity(params: CreateIdentityOptions = {}) {
|
|
158
160
|
const identity = await this.identityManager.createIdentity(params);
|
|
159
161
|
|
|
160
|
-
await this._initialize();
|
|
162
|
+
await this._initialize(new Context());
|
|
161
163
|
return identity;
|
|
162
164
|
}
|
|
163
165
|
|
|
@@ -170,7 +172,7 @@ export class ServiceContext {
|
|
|
170
172
|
private async _acceptIdentity(params: JoinIdentityParams) {
|
|
171
173
|
const identity = await this.identityManager.acceptIdentity(params);
|
|
172
174
|
|
|
173
|
-
await this._initialize();
|
|
175
|
+
await this._initialize(new Context());
|
|
174
176
|
return identity;
|
|
175
177
|
}
|
|
176
178
|
|
|
@@ -183,7 +185,8 @@ export class ServiceContext {
|
|
|
183
185
|
}
|
|
184
186
|
|
|
185
187
|
// Called when identity is created.
|
|
186
|
-
|
|
188
|
+
@Trace.span()
|
|
189
|
+
private async _initialize(ctx: Context) {
|
|
187
190
|
log('initializing spaces...');
|
|
188
191
|
const identity = this.identityManager.identity ?? failUndefined();
|
|
189
192
|
const signingContext: SigningContext = {
|
|
@@ -8,6 +8,7 @@ import { rmSync } from 'node:fs';
|
|
|
8
8
|
|
|
9
9
|
import { asyncTimeout, latch, Trigger } from '@dxos/async';
|
|
10
10
|
import { Config } from '@dxos/config';
|
|
11
|
+
import { Context } from '@dxos/context';
|
|
11
12
|
import { verifyPresentation } from '@dxos/credentials';
|
|
12
13
|
import { PublicKey } from '@dxos/keys';
|
|
13
14
|
import { MemorySignalManagerContext } from '@dxos/messaging';
|
|
@@ -30,7 +31,7 @@ describe('ClientServicesHost', () => {
|
|
|
30
31
|
|
|
31
32
|
test('queryCredentials', async () => {
|
|
32
33
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
33
|
-
await host.open();
|
|
34
|
+
await host.open(new Context());
|
|
34
35
|
afterTest(() => host.close());
|
|
35
36
|
|
|
36
37
|
await host.services.IdentityService!.createIdentity({});
|
|
@@ -49,14 +50,14 @@ describe('ClientServicesHost', () => {
|
|
|
49
50
|
|
|
50
51
|
test('write and query credentials', async () => {
|
|
51
52
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
52
|
-
await host.open();
|
|
53
|
+
await host.open(new Context());
|
|
53
54
|
afterTest(() => host.close());
|
|
54
55
|
|
|
55
56
|
await host.services.IdentityService!.createIdentity({});
|
|
56
57
|
|
|
57
58
|
const testCredential = await createMockCredential({
|
|
58
|
-
signer: host.
|
|
59
|
-
issuer: host.
|
|
59
|
+
signer: host.context.keyring,
|
|
60
|
+
issuer: host.context.identityManager.identity!.deviceKey,
|
|
60
61
|
});
|
|
61
62
|
|
|
62
63
|
// Test if Identity exposes haloSpace key.
|
|
@@ -86,14 +87,14 @@ describe('ClientServicesHost', () => {
|
|
|
86
87
|
|
|
87
88
|
test('sign presentation', async () => {
|
|
88
89
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
89
|
-
await host.open();
|
|
90
|
+
await host.open(new Context());
|
|
90
91
|
afterTest(() => host.close());
|
|
91
92
|
|
|
92
93
|
await host.services.IdentityService!.createIdentity({});
|
|
93
94
|
|
|
94
95
|
const testCredential = await createMockCredential({
|
|
95
|
-
signer: host.
|
|
96
|
-
issuer: host.
|
|
96
|
+
signer: host.context.keyring,
|
|
97
|
+
issuer: host.context.identityManager.identity!.deviceKey,
|
|
97
98
|
});
|
|
98
99
|
|
|
99
100
|
const nonce = new Uint8Array([0, 0, 0, 0]);
|
|
@@ -117,11 +118,11 @@ describe('ClientServicesHost', () => {
|
|
|
117
118
|
});
|
|
118
119
|
{
|
|
119
120
|
const host = createServiceHost(config, new MemorySignalManagerContext());
|
|
120
|
-
await host.open();
|
|
121
|
+
await host.open(new Context());
|
|
121
122
|
|
|
122
123
|
await host.services.IdentityService?.createIdentity({});
|
|
123
124
|
|
|
124
|
-
expect(host.
|
|
125
|
+
expect(host.context.storage.size).to.exist;
|
|
125
126
|
|
|
126
127
|
await asyncTimeout(host.reset(), 1000);
|
|
127
128
|
await host.close();
|
|
@@ -129,7 +130,7 @@ describe('ClientServicesHost', () => {
|
|
|
129
130
|
|
|
130
131
|
{
|
|
131
132
|
const host = createServiceHost(config, new MemorySignalManagerContext());
|
|
132
|
-
await host.open();
|
|
133
|
+
await host.open(new Context());
|
|
133
134
|
const trigger = new Trigger<Identity>();
|
|
134
135
|
|
|
135
136
|
const stream = host.services.IdentityService?.queryIdentity();
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
// Copyright 2021 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import invariant from 'tiny-invariant';
|
|
6
|
-
|
|
7
5
|
import { Event, synchronized } from '@dxos/async';
|
|
8
6
|
import { clientServiceBundle, ClientServices } from '@dxos/client-protocol';
|
|
9
7
|
import { Config } from '@dxos/config';
|
|
8
|
+
import { Context } from '@dxos/context';
|
|
10
9
|
import { DocumentModel } from '@dxos/document-model';
|
|
11
10
|
import { DataServiceImpl } from '@dxos/echo-pipeline';
|
|
11
|
+
import { invariant } from '@dxos/invariant';
|
|
12
12
|
import { PublicKey } from '@dxos/keys';
|
|
13
13
|
import { log } from '@dxos/log';
|
|
14
14
|
import { SignalManager, WebsocketSignalManager } from '@dxos/messaging';
|
|
@@ -18,6 +18,7 @@ import { trace } from '@dxos/protocols';
|
|
|
18
18
|
import { SystemStatus } from '@dxos/protocols/proto/dxos/client/services';
|
|
19
19
|
import { Storage } from '@dxos/random-access-storage';
|
|
20
20
|
import { TextModel } from '@dxos/text-model';
|
|
21
|
+
import { TRACE_PROCESSOR, trace as Trace } from '@dxos/tracing';
|
|
21
22
|
|
|
22
23
|
import { DevicesServiceImpl } from '../devices';
|
|
23
24
|
import { DevtoolsServiceImpl, DevtoolsHostEvents } from '../devtools';
|
|
@@ -29,6 +30,7 @@ import { NetworkServiceImpl } from '../network';
|
|
|
29
30
|
import { SpacesServiceImpl } from '../spaces';
|
|
30
31
|
import { createStorageObjects } from '../storage';
|
|
31
32
|
import { SystemServiceImpl } from '../system';
|
|
33
|
+
import { createDiagnostics } from './diagnostics';
|
|
32
34
|
import { ServiceContext } from './service-context';
|
|
33
35
|
import { ServiceRegistry } from './service-registry';
|
|
34
36
|
|
|
@@ -65,11 +67,13 @@ export type InitializeOptions = {
|
|
|
65
67
|
/**
|
|
66
68
|
* Remote service implementation.
|
|
67
69
|
*/
|
|
70
|
+
@Trace.resource()
|
|
68
71
|
export class ClientServicesHost {
|
|
69
72
|
private readonly _resourceLock?: ResourceLock;
|
|
70
73
|
private readonly _serviceRegistry: ServiceRegistry<ClientServices>;
|
|
71
74
|
private readonly _systemService: SystemServiceImpl;
|
|
72
75
|
private readonly _loggingService: LoggingServiceImpl;
|
|
76
|
+
private readonly _tracingService = TRACE_PROCESSOR.createTraceSender();
|
|
73
77
|
|
|
74
78
|
private _config?: Config;
|
|
75
79
|
private readonly _statusUpdate = new Event<void>();
|
|
@@ -79,17 +83,19 @@ export class ClientServicesHost {
|
|
|
79
83
|
private _storage?: Storage;
|
|
80
84
|
private _callbacks?: ClientServicesHostCallbacks;
|
|
81
85
|
|
|
82
|
-
_serviceContext!: ServiceContext;
|
|
86
|
+
private _serviceContext!: ServiceContext;
|
|
87
|
+
|
|
88
|
+
@Trace.info()
|
|
83
89
|
private _opening = false;
|
|
90
|
+
|
|
91
|
+
@Trace.info()
|
|
84
92
|
private _open = false;
|
|
85
93
|
|
|
86
94
|
constructor({
|
|
87
95
|
config,
|
|
88
96
|
modelFactory = createDefaultModelFactory(),
|
|
89
|
-
// TODO(burdon): Create ApolloLink abstraction (see Client).
|
|
90
97
|
transportFactory,
|
|
91
98
|
signalManager,
|
|
92
|
-
connectionLog,
|
|
93
99
|
storage,
|
|
94
100
|
// TODO(wittjosiah): Turn this on by default.
|
|
95
101
|
lockKey,
|
|
@@ -108,7 +114,7 @@ export class ClientServicesHost {
|
|
|
108
114
|
lockKey,
|
|
109
115
|
onAcquire: () => {
|
|
110
116
|
if (!this._opening) {
|
|
111
|
-
void this.open();
|
|
117
|
+
void this.open(new Context());
|
|
112
118
|
}
|
|
113
119
|
},
|
|
114
120
|
onRelease: () => this.close(),
|
|
@@ -117,11 +123,11 @@ export class ClientServicesHost {
|
|
|
117
123
|
|
|
118
124
|
this._systemService = new SystemServiceImpl({
|
|
119
125
|
config: this._config,
|
|
120
|
-
|
|
121
126
|
statusUpdate: this._statusUpdate,
|
|
122
|
-
|
|
123
127
|
getCurrentStatus: () => (this.isOpen ? SystemStatus.ACTIVE : SystemStatus.INACTIVE),
|
|
124
|
-
|
|
128
|
+
getDiagnostics: () => {
|
|
129
|
+
return createDiagnostics(this._serviceRegistry.services, this._serviceContext, this._config!);
|
|
130
|
+
},
|
|
125
131
|
onUpdateStatus: async (status: SystemStatus) => {
|
|
126
132
|
if (!this.isOpen && status === SystemStatus.ACTIVE) {
|
|
127
133
|
await this._resourceLock?.acquire();
|
|
@@ -129,7 +135,6 @@ export class ClientServicesHost {
|
|
|
129
135
|
await this._resourceLock?.release();
|
|
130
136
|
}
|
|
131
137
|
},
|
|
132
|
-
|
|
133
138
|
onReset: async () => {
|
|
134
139
|
await this.reset();
|
|
135
140
|
},
|
|
@@ -137,9 +142,9 @@ export class ClientServicesHost {
|
|
|
137
142
|
|
|
138
143
|
this._loggingService = new LoggingServiceImpl();
|
|
139
144
|
|
|
140
|
-
// TODO(burdon): Start to think of DMG (dynamic services).
|
|
141
145
|
this._serviceRegistry = new ServiceRegistry<ClientServices>(clientServiceBundle, {
|
|
142
146
|
SystemService: this._systemService,
|
|
147
|
+
TracingService: this._tracingService,
|
|
143
148
|
});
|
|
144
149
|
}
|
|
145
150
|
|
|
@@ -147,6 +152,14 @@ export class ClientServicesHost {
|
|
|
147
152
|
return this._open;
|
|
148
153
|
}
|
|
149
154
|
|
|
155
|
+
get config() {
|
|
156
|
+
return this._config;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
get context() {
|
|
160
|
+
return this._serviceContext;
|
|
161
|
+
}
|
|
162
|
+
|
|
150
163
|
get serviceRegistry() {
|
|
151
164
|
return this._serviceRegistry;
|
|
152
165
|
}
|
|
@@ -166,10 +179,10 @@ export class ClientServicesHost {
|
|
|
166
179
|
*/
|
|
167
180
|
initialize({ config, ...options }: InitializeOptions) {
|
|
168
181
|
invariant(!this._open, 'service host is open');
|
|
182
|
+
log.info('initializing...');
|
|
169
183
|
|
|
170
184
|
if (config) {
|
|
171
185
|
invariant(!this._config, 'config already set');
|
|
172
|
-
|
|
173
186
|
this._config = config;
|
|
174
187
|
if (!this._storage) {
|
|
175
188
|
this._storage = createStorageObjects(config.get('runtime.client.storage', {})!).storage;
|
|
@@ -191,16 +204,19 @@ export class ClientServicesHost {
|
|
|
191
204
|
transportFactory,
|
|
192
205
|
signalManager,
|
|
193
206
|
});
|
|
207
|
+
|
|
208
|
+
log.info('initialized');
|
|
194
209
|
}
|
|
195
210
|
|
|
196
211
|
@synchronized
|
|
197
|
-
|
|
212
|
+
@Trace.span()
|
|
213
|
+
async open(ctx: Context) {
|
|
198
214
|
if (this._open) {
|
|
199
215
|
return;
|
|
200
216
|
}
|
|
201
217
|
|
|
202
218
|
const traceId = PublicKey.random().toHex();
|
|
203
|
-
log.trace('dxos.
|
|
219
|
+
log.trace('dxos.client-services.host.open', trace.begin({ id: traceId }));
|
|
204
220
|
|
|
205
221
|
invariant(this._config, 'config not set');
|
|
206
222
|
invariant(this._storage, 'storage not set');
|
|
@@ -208,13 +224,11 @@ export class ClientServicesHost {
|
|
|
208
224
|
invariant(this._networkManager, 'network manager not set');
|
|
209
225
|
|
|
210
226
|
this._opening = true;
|
|
211
|
-
log('opening...', { lockKey: this._resourceLock?.lockKey });
|
|
227
|
+
log.info('opening...', { lockKey: this._resourceLock?.lockKey });
|
|
212
228
|
await this._resourceLock?.acquire();
|
|
213
229
|
|
|
214
230
|
await this._loggingService.open();
|
|
215
231
|
|
|
216
|
-
// TODO(wittjosiah): Make re-entrant.
|
|
217
|
-
// TODO(burdon): Break into components.
|
|
218
232
|
this._serviceContext = new ServiceContext(
|
|
219
233
|
this._storage,
|
|
220
234
|
this._networkManager,
|
|
@@ -222,7 +236,6 @@ export class ClientServicesHost {
|
|
|
222
236
|
this._modelFactory,
|
|
223
237
|
);
|
|
224
238
|
|
|
225
|
-
// TODO(burdon): Start to think of DMG (dynamic services).
|
|
226
239
|
this._serviceRegistry.setServices({
|
|
227
240
|
SystemService: this._systemService,
|
|
228
241
|
|
|
@@ -249,6 +262,7 @@ export class ClientServicesHost {
|
|
|
249
262
|
NetworkService: new NetworkServiceImpl(this._serviceContext.networkManager, this._serviceContext.signalManager),
|
|
250
263
|
|
|
251
264
|
LoggingService: this._loggingService,
|
|
265
|
+
TracingService: this._tracingService,
|
|
252
266
|
|
|
253
267
|
// TODO(burdon): Move to new protobuf definitions.
|
|
254
268
|
DevtoolsHost: new DevtoolsServiceImpl({
|
|
@@ -258,29 +272,30 @@ export class ClientServicesHost {
|
|
|
258
272
|
}),
|
|
259
273
|
});
|
|
260
274
|
|
|
261
|
-
await this._serviceContext.open();
|
|
275
|
+
await this._serviceContext.open(ctx);
|
|
262
276
|
this._opening = false;
|
|
263
277
|
this._open = true;
|
|
264
278
|
this._statusUpdate.emit();
|
|
265
279
|
const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
|
|
266
|
-
log('opened', { deviceKey });
|
|
267
|
-
log.trace('dxos.
|
|
280
|
+
log.info('opened', { deviceKey });
|
|
281
|
+
log.trace('dxos.client-services.host.open', trace.end({ id: traceId }));
|
|
268
282
|
}
|
|
269
283
|
|
|
270
284
|
@synchronized
|
|
285
|
+
@Trace.span()
|
|
271
286
|
async close() {
|
|
272
287
|
if (!this._open) {
|
|
273
288
|
return;
|
|
274
289
|
}
|
|
275
290
|
|
|
276
291
|
const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
|
|
277
|
-
log('closing...', { deviceKey });
|
|
292
|
+
log.info('closing...', { deviceKey });
|
|
278
293
|
this._serviceRegistry.setServices({ SystemService: this._systemService });
|
|
279
294
|
await this._loggingService.close();
|
|
280
295
|
await this._serviceContext.close();
|
|
281
296
|
this._open = false;
|
|
282
297
|
this._statusUpdate.emit();
|
|
283
|
-
log('closed', { deviceKey });
|
|
298
|
+
log.info('closed', { deviceKey });
|
|
284
299
|
}
|
|
285
300
|
|
|
286
301
|
async reset() {
|
|
@@ -7,6 +7,7 @@ import { expect } from 'chai';
|
|
|
7
7
|
import { Event } from '@dxos/async';
|
|
8
8
|
import { ClientServices } from '@dxos/client-protocol';
|
|
9
9
|
import { Config } from '@dxos/config';
|
|
10
|
+
import { Context } from '@dxos/context';
|
|
10
11
|
import { log } from '@dxos/log';
|
|
11
12
|
import { schema } from '@dxos/protocols';
|
|
12
13
|
import { SystemService, SystemStatus } from '@dxos/protocols/proto/dxos/client/services';
|
|
@@ -31,12 +32,13 @@ describe('service registry', () => {
|
|
|
31
32
|
test('builds a service registry', async () => {
|
|
32
33
|
const remoteSource = 'https://remote.source';
|
|
33
34
|
const serviceContext = createServiceContext();
|
|
34
|
-
await serviceContext.open();
|
|
35
|
+
await serviceContext.open(new Context());
|
|
35
36
|
|
|
36
37
|
const serviceRegistry = new ServiceRegistry(serviceBundle, {
|
|
37
38
|
SystemService: new SystemServiceImpl({
|
|
38
39
|
config: new Config({ runtime: { client: { remoteSource } } }),
|
|
39
40
|
getCurrentStatus: () => SystemStatus.ACTIVE,
|
|
41
|
+
getDiagnostics: async () => ({}),
|
|
40
42
|
onReset: () => {},
|
|
41
43
|
onUpdateStatus: () => {},
|
|
42
44
|
statusUpdate: new Event(),
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { PublicKey } from '@dxos/keys';
|
|
6
|
+
import { humanize } from '@dxos/util';
|
|
7
|
+
|
|
8
|
+
// TODO(burdon): Move to util.
|
|
9
|
+
|
|
10
|
+
export type JsonStringifyOptions = {
|
|
11
|
+
truncate?: boolean;
|
|
12
|
+
humanize?: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const jsonStringify = <T>(data: T, options: JsonStringifyOptions) => {
|
|
16
|
+
if (options.humanize || options.truncate) {
|
|
17
|
+
return JSON.parse(JSON.stringify(data, jsonKeyReplacer(options)));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return data;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const jsonKeyReplacer = (options: JsonStringifyOptions) => (key: string, value: any) => {
|
|
24
|
+
if (typeof value === 'string') {
|
|
25
|
+
// Infer if key.
|
|
26
|
+
const key = PublicKey.fromHex(value);
|
|
27
|
+
if (key.toHex() === value) {
|
|
28
|
+
return options.humanize ? humanize(key) : key.truncate();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return value;
|
|
33
|
+
};
|
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import invariant from 'tiny-invariant';
|
|
6
|
-
|
|
7
5
|
import { Event, synchronized, trackLeaks } from '@dxos/async';
|
|
8
6
|
import { cancelWithContext, Context } from '@dxos/context';
|
|
9
7
|
import { CredentialSigner, getCredentialAssertion } from '@dxos/credentials';
|
|
10
8
|
import { DataServiceSubscriptions, MetadataStore, Space, SpaceManager } from '@dxos/echo-pipeline';
|
|
11
9
|
import { FeedStore } from '@dxos/feed-store';
|
|
10
|
+
import { invariant } from '@dxos/invariant';
|
|
12
11
|
import { Keyring } from '@dxos/keyring';
|
|
13
12
|
import { PublicKey } from '@dxos/keys';
|
|
14
13
|
import { log } from '@dxos/log';
|
|
@@ -185,7 +184,7 @@ export class DataSpaceManager {
|
|
|
185
184
|
localPeerId: this._signingContext.deviceKey,
|
|
186
185
|
});
|
|
187
186
|
const presence = new Presence({
|
|
188
|
-
announceInterval:
|
|
187
|
+
announceInterval: 1_000,
|
|
189
188
|
offlineTimeout: 5_000, // TODO(burdon): Config.
|
|
190
189
|
identityKey: this._signingContext.identityKey,
|
|
191
190
|
gossip,
|
|
@@ -145,7 +145,7 @@ export class DataSpace {
|
|
|
145
145
|
private async _open() {
|
|
146
146
|
await this._notarizationPlugin.open();
|
|
147
147
|
await this._inner.spaceState.addCredentialProcessor(this._notarizationPlugin);
|
|
148
|
-
await this._inner.open();
|
|
148
|
+
await this._inner.open(new Context());
|
|
149
149
|
this._state = SpaceState.CONTROL_ONLY;
|
|
150
150
|
this.stateUpdate.emit();
|
|
151
151
|
this.metrics = {};
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import invariant from 'tiny-invariant';
|
|
6
|
-
|
|
7
5
|
import { DeferredTask, Event, scheduleTask, sleep, TimeoutError, Trigger } from '@dxos/async';
|
|
8
6
|
import { Context, rejectOnDispose } from '@dxos/context';
|
|
9
7
|
import { CredentialProcessor } from '@dxos/credentials';
|
|
10
8
|
import { FeedWriter } from '@dxos/feed-store';
|
|
9
|
+
import { invariant } from '@dxos/invariant';
|
|
11
10
|
import { PublicKey } from '@dxos/keys';
|
|
12
11
|
import { log } from '@dxos/log';
|
|
13
12
|
import { schema } from '@dxos/protocols';
|
|
@@ -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 { Space, SpacesService } from '@dxos/protocols/proto/dxos/client/services';
|
|
11
12
|
import { afterEach, afterTest, beforeEach, describe, test } from '@dxos/test';
|
|
@@ -22,7 +23,7 @@ describe('SpacesService', () => {
|
|
|
22
23
|
|
|
23
24
|
beforeEach(async () => {
|
|
24
25
|
serviceContext = createServiceContext();
|
|
25
|
-
await serviceContext.open();
|
|
26
|
+
await serviceContext.open(new Context());
|
|
26
27
|
spacesService = new SpacesServiceImpl(
|
|
27
28
|
serviceContext.identityManager,
|
|
28
29
|
serviceContext.spaceManager,
|
|
@@ -185,19 +185,25 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
185
185
|
targetDataTimeframe: space.dataPipeline.pipelineState?.targetTimeframe,
|
|
186
186
|
totalDataTimeframe: space.dataPipeline.pipelineState?.endTimeframe,
|
|
187
187
|
},
|
|
188
|
-
members: Array.from(space.inner.spaceState.members.values()).map((member) =>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
188
|
+
members: Array.from(space.inner.spaceState.members.values()).map((member) => {
|
|
189
|
+
const peers = space.presence.getPeersOnline().filter(({ identityKey }) => identityKey.equals(member.key));
|
|
190
|
+
const isMe = this._identityManager.identity?.identityKey.equals(member.key);
|
|
191
|
+
|
|
192
|
+
if (isMe) {
|
|
193
|
+
peers.push(space.presence.getLocalState());
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
identity: {
|
|
198
|
+
identityKey: member.key,
|
|
199
|
+
profile: {
|
|
200
|
+
displayName: member.assertion.profile?.displayName ?? humanize(member.key),
|
|
201
|
+
},
|
|
193
202
|
},
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
? SpaceMember.PresenceState.ONLINE
|
|
199
|
-
: SpaceMember.PresenceState.OFFLINE,
|
|
200
|
-
})),
|
|
203
|
+
presence: isMe || peers.length > 0 ? SpaceMember.PresenceState.ONLINE : SpaceMember.PresenceState.OFFLINE,
|
|
204
|
+
peerStates: peers,
|
|
205
|
+
};
|
|
206
|
+
}),
|
|
201
207
|
creator: space.inner.spaceState.creator?.key,
|
|
202
208
|
cache: space.cache,
|
|
203
209
|
metrics: space.metrics,
|
|
@@ -6,18 +6,22 @@ import { Event } from '@dxos/async';
|
|
|
6
6
|
import { Stream } from '@dxos/codec-protobuf';
|
|
7
7
|
import { Config } from '@dxos/config';
|
|
8
8
|
import {
|
|
9
|
+
GetDiagnosticsRequest,
|
|
9
10
|
SystemService,
|
|
10
11
|
SystemStatus,
|
|
11
12
|
UpdateStatusRequest,
|
|
12
13
|
QueryStatusRequest,
|
|
13
14
|
QueryStatusResponse,
|
|
14
15
|
} from '@dxos/protocols/proto/dxos/client/services';
|
|
15
|
-
import { MaybePromise } from '@dxos/util';
|
|
16
|
+
import { jsonKeyReplacer, MaybePromise } from '@dxos/util';
|
|
17
|
+
|
|
18
|
+
import { Diagnostics } from '../services';
|
|
16
19
|
|
|
17
20
|
export type SystemServiceOptions = {
|
|
18
21
|
config?: Config;
|
|
19
22
|
statusUpdate: Event<void>;
|
|
20
23
|
getCurrentStatus: () => SystemStatus;
|
|
24
|
+
getDiagnostics: () => Promise<Partial<Diagnostics>>;
|
|
21
25
|
onUpdateStatus: (status: SystemStatus) => MaybePromise<void>;
|
|
22
26
|
onReset: () => MaybePromise<void>;
|
|
23
27
|
};
|
|
@@ -28,11 +32,20 @@ export class SystemServiceImpl implements SystemService {
|
|
|
28
32
|
private readonly _getCurrentStatus: SystemServiceOptions['getCurrentStatus'];
|
|
29
33
|
private readonly _onUpdateStatus: SystemServiceOptions['onUpdateStatus'];
|
|
30
34
|
private readonly _onReset: SystemServiceOptions['onReset'];
|
|
35
|
+
private readonly _getDiagnostics: SystemServiceOptions['getDiagnostics'];
|
|
31
36
|
|
|
32
|
-
constructor({
|
|
37
|
+
constructor({
|
|
38
|
+
config,
|
|
39
|
+
statusUpdate,
|
|
40
|
+
getDiagnostics,
|
|
41
|
+
onUpdateStatus,
|
|
42
|
+
getCurrentStatus,
|
|
43
|
+
onReset,
|
|
44
|
+
}: SystemServiceOptions) {
|
|
33
45
|
this._config = config;
|
|
34
46
|
this._statusUpdate = statusUpdate;
|
|
35
47
|
this._getCurrentStatus = getCurrentStatus;
|
|
48
|
+
this._getDiagnostics = getDiagnostics;
|
|
36
49
|
this._onUpdateStatus = onUpdateStatus;
|
|
37
50
|
this._onReset = onReset;
|
|
38
51
|
}
|
|
@@ -41,6 +54,25 @@ export class SystemServiceImpl implements SystemService {
|
|
|
41
54
|
return this._config?.values ?? {};
|
|
42
55
|
}
|
|
43
56
|
|
|
57
|
+
/**
|
|
58
|
+
* NOTE: Since this is serialized as a JSON object, we allow the option to serialize keys.
|
|
59
|
+
*/
|
|
60
|
+
async getDiagnostics({ keys }: GetDiagnosticsRequest = {}) {
|
|
61
|
+
const diagnostics = await this._getDiagnostics();
|
|
62
|
+
return {
|
|
63
|
+
timestamp: new Date(),
|
|
64
|
+
diagnostics: JSON.parse(
|
|
65
|
+
JSON.stringify(
|
|
66
|
+
diagnostics,
|
|
67
|
+
jsonKeyReplacer({
|
|
68
|
+
truncate: keys === GetDiagnosticsRequest.KEY_OPTION.TRUNCATE,
|
|
69
|
+
humanize: keys === GetDiagnosticsRequest.KEY_OPTION.HUMANIZE,
|
|
70
|
+
}),
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
44
76
|
async updateStatus({ status }: UpdateStatusRequest) {
|
|
45
77
|
await this._onUpdateStatus(status);
|
|
46
78
|
}
|