@dxos/client-services 0.1.56-main.83e9b8a → 0.1.56-main.a25bb93
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-P26UKXYR.mjs → chunk-MAHWP5I6.mjs} +970 -383
- package/dist/lib/browser/chunk-MAHWP5I6.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +50 -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 +12 -3
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +1113 -510
- 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 +1260 -667
- package/dist/lib/node/packlets/testing/index.cjs.map +4 -4
- 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.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/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.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +4 -2
- 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/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/shell-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 +33 -33
- package/src/packlets/identity/authenticator.test.ts +2 -2
- package/src/packlets/identity/identity-manager.ts +1 -2
- package/src/packlets/identity/identity-service.ts +1 -2
- package/src/packlets/identity/identity.ts +1 -2
- 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/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 +3 -5
- package/src/packlets/services/service-host.test.ts +5 -5
- package/src/packlets/services/service-host.ts +23 -20
- package/src/packlets/services/service-registry.test.ts +1 -0
- package/src/packlets/services/util.ts +33 -0
- package/src/packlets/spaces/data-space-manager.ts +1 -2
- package/src/packlets/spaces/notarization-plugin.ts +1 -2
- 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/vault/shell-runtime.ts +1 -2
- package/src/packlets/vault/worker-session.ts +7 -3
- package/src/version.ts +1 -0
- package/dist/lib/browser/chunk-P26UKXYR.mjs.map +0 -7
|
@@ -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
|
};
|
|
@@ -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
|
+
};
|
|
@@ -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,8 +2,6 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import invariant from 'tiny-invariant';
|
|
6
|
-
|
|
7
5
|
import { Trigger } from '@dxos/async';
|
|
8
6
|
import { CredentialProcessor, getCredentialAssertion } from '@dxos/credentials';
|
|
9
7
|
import { failUndefined } from '@dxos/debug';
|
|
@@ -15,6 +13,7 @@ import {
|
|
|
15
13
|
SnapshotStore,
|
|
16
14
|
} from '@dxos/echo-pipeline';
|
|
17
15
|
import { FeedFactory, FeedStore } from '@dxos/feed-store';
|
|
16
|
+
import { invariant } from '@dxos/invariant';
|
|
18
17
|
import { Keyring } from '@dxos/keyring';
|
|
19
18
|
import { PublicKey } from '@dxos/keys';
|
|
20
19
|
import { log } from '@dxos/log';
|
|
@@ -123,11 +122,10 @@ export class ServiceContext {
|
|
|
123
122
|
}
|
|
124
123
|
|
|
125
124
|
async open() {
|
|
126
|
-
log.trace('dxos.sdk.service-context.open', trace.begin({ id: this._instanceId }));
|
|
127
|
-
|
|
128
125
|
await this._checkStorageVersion();
|
|
129
126
|
|
|
130
127
|
log('opening...');
|
|
128
|
+
log.trace('dxos.sdk.service-context.open', trace.begin({ id: this._instanceId }));
|
|
131
129
|
await this.signalManager.open();
|
|
132
130
|
await this.networkManager.open();
|
|
133
131
|
await this.spaceManager.open();
|
|
@@ -135,8 +133,8 @@ export class ServiceContext {
|
|
|
135
133
|
if (this.identityManager.identity) {
|
|
136
134
|
await this._initialize();
|
|
137
135
|
}
|
|
138
|
-
log('opened');
|
|
139
136
|
log.trace('dxos.sdk.service-context.open', trace.end({ id: this._instanceId }));
|
|
137
|
+
log('opened');
|
|
140
138
|
}
|
|
141
139
|
|
|
142
140
|
async close() {
|
|
@@ -55,8 +55,8 @@ describe('ClientServicesHost', () => {
|
|
|
55
55
|
await host.services.IdentityService!.createIdentity({});
|
|
56
56
|
|
|
57
57
|
const testCredential = await createMockCredential({
|
|
58
|
-
signer: host.
|
|
59
|
-
issuer: host.
|
|
58
|
+
signer: host.context.keyring,
|
|
59
|
+
issuer: host.context.identityManager.identity!.deviceKey,
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
// Test if Identity exposes haloSpace key.
|
|
@@ -92,8 +92,8 @@ describe('ClientServicesHost', () => {
|
|
|
92
92
|
await host.services.IdentityService!.createIdentity({});
|
|
93
93
|
|
|
94
94
|
const testCredential = await createMockCredential({
|
|
95
|
-
signer: host.
|
|
96
|
-
issuer: host.
|
|
95
|
+
signer: host.context.keyring,
|
|
96
|
+
issuer: host.context.identityManager.identity!.deviceKey,
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
const nonce = new Uint8Array([0, 0, 0, 0]);
|
|
@@ -121,7 +121,7 @@ describe('ClientServicesHost', () => {
|
|
|
121
121
|
|
|
122
122
|
await host.services.IdentityService?.createIdentity({});
|
|
123
123
|
|
|
124
|
-
expect(host.
|
|
124
|
+
expect(host.context.storage.size).to.exist;
|
|
125
125
|
|
|
126
126
|
await asyncTimeout(host.reset(), 1000);
|
|
127
127
|
await host.close();
|
|
@@ -2,13 +2,12 @@
|
|
|
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';
|
|
10
8
|
import { DocumentModel } from '@dxos/document-model';
|
|
11
9
|
import { DataServiceImpl } from '@dxos/echo-pipeline';
|
|
10
|
+
import { invariant } from '@dxos/invariant';
|
|
12
11
|
import { PublicKey } from '@dxos/keys';
|
|
13
12
|
import { log } from '@dxos/log';
|
|
14
13
|
import { SignalManager, WebsocketSignalManager } from '@dxos/messaging';
|
|
@@ -29,6 +28,7 @@ import { NetworkServiceImpl } from '../network';
|
|
|
29
28
|
import { SpacesServiceImpl } from '../spaces';
|
|
30
29
|
import { createStorageObjects } from '../storage';
|
|
31
30
|
import { SystemServiceImpl } from '../system';
|
|
31
|
+
import { createDiagnostics } from './diagnostics';
|
|
32
32
|
import { ServiceContext } from './service-context';
|
|
33
33
|
import { ServiceRegistry } from './service-registry';
|
|
34
34
|
|
|
@@ -79,17 +79,15 @@ export class ClientServicesHost {
|
|
|
79
79
|
private _storage?: Storage;
|
|
80
80
|
private _callbacks?: ClientServicesHostCallbacks;
|
|
81
81
|
|
|
82
|
-
_serviceContext!: ServiceContext;
|
|
82
|
+
private _serviceContext!: ServiceContext;
|
|
83
83
|
private _opening = false;
|
|
84
84
|
private _open = false;
|
|
85
85
|
|
|
86
86
|
constructor({
|
|
87
87
|
config,
|
|
88
88
|
modelFactory = createDefaultModelFactory(),
|
|
89
|
-
// TODO(burdon): Create ApolloLink abstraction (see Client).
|
|
90
89
|
transportFactory,
|
|
91
90
|
signalManager,
|
|
92
|
-
connectionLog,
|
|
93
91
|
storage,
|
|
94
92
|
// TODO(wittjosiah): Turn this on by default.
|
|
95
93
|
lockKey,
|
|
@@ -117,11 +115,11 @@ export class ClientServicesHost {
|
|
|
117
115
|
|
|
118
116
|
this._systemService = new SystemServiceImpl({
|
|
119
117
|
config: this._config,
|
|
120
|
-
|
|
121
118
|
statusUpdate: this._statusUpdate,
|
|
122
|
-
|
|
123
119
|
getCurrentStatus: () => (this.isOpen ? SystemStatus.ACTIVE : SystemStatus.INACTIVE),
|
|
124
|
-
|
|
120
|
+
getDiagnostics: () => {
|
|
121
|
+
return createDiagnostics(this._serviceRegistry.services, this._serviceContext, this._config!);
|
|
122
|
+
},
|
|
125
123
|
onUpdateStatus: async (status: SystemStatus) => {
|
|
126
124
|
if (!this.isOpen && status === SystemStatus.ACTIVE) {
|
|
127
125
|
await this._resourceLock?.acquire();
|
|
@@ -129,7 +127,6 @@ export class ClientServicesHost {
|
|
|
129
127
|
await this._resourceLock?.release();
|
|
130
128
|
}
|
|
131
129
|
},
|
|
132
|
-
|
|
133
130
|
onReset: async () => {
|
|
134
131
|
await this.reset();
|
|
135
132
|
},
|
|
@@ -137,7 +134,6 @@ export class ClientServicesHost {
|
|
|
137
134
|
|
|
138
135
|
this._loggingService = new LoggingServiceImpl();
|
|
139
136
|
|
|
140
|
-
// TODO(burdon): Start to think of DMG (dynamic services).
|
|
141
137
|
this._serviceRegistry = new ServiceRegistry<ClientServices>(clientServiceBundle, {
|
|
142
138
|
SystemService: this._systemService,
|
|
143
139
|
});
|
|
@@ -147,6 +143,14 @@ export class ClientServicesHost {
|
|
|
147
143
|
return this._open;
|
|
148
144
|
}
|
|
149
145
|
|
|
146
|
+
get config() {
|
|
147
|
+
return this._config;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
get context() {
|
|
151
|
+
return this._serviceContext;
|
|
152
|
+
}
|
|
153
|
+
|
|
150
154
|
get serviceRegistry() {
|
|
151
155
|
return this._serviceRegistry;
|
|
152
156
|
}
|
|
@@ -166,10 +170,10 @@ export class ClientServicesHost {
|
|
|
166
170
|
*/
|
|
167
171
|
initialize({ config, ...options }: InitializeOptions) {
|
|
168
172
|
invariant(!this._open, 'service host is open');
|
|
173
|
+
log.info('initializing...');
|
|
169
174
|
|
|
170
175
|
if (config) {
|
|
171
176
|
invariant(!this._config, 'config already set');
|
|
172
|
-
|
|
173
177
|
this._config = config;
|
|
174
178
|
if (!this._storage) {
|
|
175
179
|
this._storage = createStorageObjects(config.get('runtime.client.storage', {})!).storage;
|
|
@@ -191,6 +195,8 @@ export class ClientServicesHost {
|
|
|
191
195
|
transportFactory,
|
|
192
196
|
signalManager,
|
|
193
197
|
});
|
|
198
|
+
|
|
199
|
+
log.info('initialized');
|
|
194
200
|
}
|
|
195
201
|
|
|
196
202
|
@synchronized
|
|
@@ -200,7 +206,7 @@ export class ClientServicesHost {
|
|
|
200
206
|
}
|
|
201
207
|
|
|
202
208
|
const traceId = PublicKey.random().toHex();
|
|
203
|
-
log.trace('dxos.
|
|
209
|
+
log.trace('dxos.client-services.host.open', trace.begin({ id: traceId }));
|
|
204
210
|
|
|
205
211
|
invariant(this._config, 'config not set');
|
|
206
212
|
invariant(this._storage, 'storage not set');
|
|
@@ -208,13 +214,11 @@ export class ClientServicesHost {
|
|
|
208
214
|
invariant(this._networkManager, 'network manager not set');
|
|
209
215
|
|
|
210
216
|
this._opening = true;
|
|
211
|
-
log('opening...', { lockKey: this._resourceLock?.lockKey });
|
|
217
|
+
log.info('opening...', { lockKey: this._resourceLock?.lockKey });
|
|
212
218
|
await this._resourceLock?.acquire();
|
|
213
219
|
|
|
214
220
|
await this._loggingService.open();
|
|
215
221
|
|
|
216
|
-
// TODO(wittjosiah): Make re-entrant.
|
|
217
|
-
// TODO(burdon): Break into components.
|
|
218
222
|
this._serviceContext = new ServiceContext(
|
|
219
223
|
this._storage,
|
|
220
224
|
this._networkManager,
|
|
@@ -222,7 +226,6 @@ export class ClientServicesHost {
|
|
|
222
226
|
this._modelFactory,
|
|
223
227
|
);
|
|
224
228
|
|
|
225
|
-
// TODO(burdon): Start to think of DMG (dynamic services).
|
|
226
229
|
this._serviceRegistry.setServices({
|
|
227
230
|
SystemService: this._systemService,
|
|
228
231
|
|
|
@@ -263,8 +266,8 @@ export class ClientServicesHost {
|
|
|
263
266
|
this._open = true;
|
|
264
267
|
this._statusUpdate.emit();
|
|
265
268
|
const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
|
|
266
|
-
log('opened', { deviceKey });
|
|
267
|
-
log.trace('dxos.
|
|
269
|
+
log.info('opened', { deviceKey });
|
|
270
|
+
log.trace('dxos.client-services.host.open', trace.end({ id: traceId }));
|
|
268
271
|
}
|
|
269
272
|
|
|
270
273
|
@synchronized
|
|
@@ -274,13 +277,13 @@ export class ClientServicesHost {
|
|
|
274
277
|
}
|
|
275
278
|
|
|
276
279
|
const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
|
|
277
|
-
log('closing...', { deviceKey });
|
|
280
|
+
log.info('closing...', { deviceKey });
|
|
278
281
|
this._serviceRegistry.setServices({ SystemService: this._systemService });
|
|
279
282
|
await this._loggingService.close();
|
|
280
283
|
await this._serviceContext.close();
|
|
281
284
|
this._open = false;
|
|
282
285
|
this._statusUpdate.emit();
|
|
283
|
-
log('closed', { deviceKey });
|
|
286
|
+
log.info('closed', { deviceKey });
|
|
284
287
|
}
|
|
285
288
|
|
|
286
289
|
async reset() {
|
|
@@ -37,6 +37,7 @@ describe('service registry', () => {
|
|
|
37
37
|
SystemService: new SystemServiceImpl({
|
|
38
38
|
config: new Config({ runtime: { client: { remoteSource } } }),
|
|
39
39
|
getCurrentStatus: () => SystemStatus.ACTIVE,
|
|
40
|
+
getDiagnostics: async () => ({}),
|
|
40
41
|
onReset: () => {},
|
|
41
42
|
onUpdateStatus: () => {},
|
|
42
43
|
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';
|
|
@@ -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';
|