@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.
Files changed (71) hide show
  1. package/dist/lib/browser/{chunk-P26UKXYR.mjs → chunk-MAHWP5I6.mjs} +970 -383
  2. package/dist/lib/browser/chunk-MAHWP5I6.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +50 -31
  4. package/dist/lib/browser/index.mjs.map +3 -3
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/packlets/testing/index.mjs +12 -3
  7. package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/index.cjs +1113 -510
  9. package/dist/lib/node/index.cjs.map +4 -4
  10. package/dist/lib/node/meta.json +1 -1
  11. package/dist/lib/node/packlets/testing/index.cjs +1260 -667
  12. package/dist/lib/node/packlets/testing/index.cjs.map +4 -4
  13. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  14. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  15. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  16. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
  17. package/dist/types/src/packlets/invitations/invitation-extension.d.ts.map +1 -1
  18. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  19. package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
  20. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  21. package/dist/types/src/packlets/logging/logging-service.d.ts +6 -2
  22. package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
  23. package/dist/types/src/packlets/services/diagnostics.d.ts +46 -0
  24. package/dist/types/src/packlets/services/diagnostics.d.ts.map +1 -0
  25. package/dist/types/src/packlets/services/index.d.ts +1 -0
  26. package/dist/types/src/packlets/services/index.d.ts.map +1 -1
  27. package/dist/types/src/packlets/services/platform.d.ts +16 -0
  28. package/dist/types/src/packlets/services/platform.d.ts.map +1 -0
  29. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  30. package/dist/types/src/packlets/services/service-host.d.ts +4 -2
  31. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  32. package/dist/types/src/packlets/services/util.d.ts +6 -0
  33. package/dist/types/src/packlets/services/util.d.ts.map +1 -0
  34. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  35. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
  36. package/dist/types/src/packlets/system/system-service.d.ts +12 -2
  37. package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
  38. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  39. package/dist/types/src/packlets/vault/shell-runtime.d.ts.map +1 -1
  40. package/dist/types/src/packlets/vault/worker-session.d.ts.map +1 -1
  41. package/dist/types/src/version.d.ts +2 -0
  42. package/dist/types/src/version.d.ts.map +1 -0
  43. package/package.json +33 -33
  44. package/src/packlets/identity/authenticator.test.ts +2 -2
  45. package/src/packlets/identity/identity-manager.ts +1 -2
  46. package/src/packlets/identity/identity-service.ts +1 -2
  47. package/src/packlets/identity/identity.ts +1 -2
  48. package/src/packlets/invitations/device-invitation-protocol.ts +1 -2
  49. package/src/packlets/invitations/invitation-extension.ts +1 -2
  50. package/src/packlets/invitations/invitations-handler.ts +1 -2
  51. package/src/packlets/invitations/invitations-service.ts +1 -2
  52. package/src/packlets/invitations/space-invitation-protocol.ts +1 -2
  53. package/src/packlets/locks/node.ts +1 -1
  54. package/src/packlets/logging/logging-service.ts +17 -9
  55. package/src/packlets/services/diagnostics.ts +211 -0
  56. package/src/packlets/services/index.ts +1 -0
  57. package/src/packlets/services/platform.ts +48 -0
  58. package/src/packlets/services/service-context.ts +3 -5
  59. package/src/packlets/services/service-host.test.ts +5 -5
  60. package/src/packlets/services/service-host.ts +23 -20
  61. package/src/packlets/services/service-registry.test.ts +1 -0
  62. package/src/packlets/services/util.ts +33 -0
  63. package/src/packlets/spaces/data-space-manager.ts +1 -2
  64. package/src/packlets/spaces/notarization-plugin.ts +1 -2
  65. package/src/packlets/system/system-service.test.ts +1 -0
  66. package/src/packlets/system/system-service.ts +34 -2
  67. package/src/packlets/testing/invitation-utils.ts +1 -2
  68. package/src/packlets/vault/shell-runtime.ts +1 -2
  69. package/src/packlets/vault/worker-session.ts +7 -3
  70. package/src/version.ts +1 -0
  71. 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
- queryMetrics({ interval }: QueryMetricsRequest): Stream<Metrics> {
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 createNumericalValues = (key: string) => {
51
- const consume = tracer.get(key) ?? [];
52
- return { key, stats: numericalValues(consume, 'duration') };
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
- createNumericalValues('dxos.echo.pipeline.control'),
61
- createNumericalValues('dxos.echo.pipeline.data'),
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(metrics);
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
+ };
@@ -3,6 +3,7 @@
3
3
  //
4
4
 
5
5
  export * from './client-rpc-server';
6
+ export * from './diagnostics';
6
7
  export * from './service-context';
7
8
  export * from './service-host';
8
9
  export * from './service-registry';
@@ -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._serviceContext.keyring,
59
- issuer: host._serviceContext.identityManager.identity!.deviceKey,
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._serviceContext.keyring,
96
- issuer: host._serviceContext.identityManager.identity!.deviceKey,
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._serviceContext.storage.size).to.exist;
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.sdk.client-services-host.open', trace.begin({ id: traceId }));
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.sdk.client-services-host.open', trace.end({ id: traceId }));
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';
@@ -35,6 +35,7 @@ describe('SystemService', () => {
35
35
  config,
36
36
  statusUpdate,
37
37
  getCurrentStatus: () => currentStatus,
38
+ getDiagnostics: async () => ({}),
38
39
  onUpdateStatus: (status) => {
39
40
  updateStatus.wake(status);
40
41
  },