@dxos/client-services 0.4.9 → 0.4.10-main.06ef97a

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 (89) hide show
  1. package/dist/lib/browser/{chunk-RQ33OGAG.mjs → chunk-XCFXYSCO.mjs} +788 -531
  2. package/dist/lib/browser/chunk-XCFXYSCO.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +58 -20
  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 +6 -4
  7. package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/{chunk-CBSM4HU4.cjs → chunk-ENGA4MHO.cjs} +726 -556
  9. package/dist/lib/node/chunk-ENGA4MHO.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +95 -57
  11. package/dist/lib/node/index.cjs.map +3 -3
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/packlets/testing/index.cjs +10 -8
  14. package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
  15. package/dist/types/src/index.d.ts +1 -0
  16. package/dist/types/src/index.d.ts.map +1 -1
  17. package/dist/types/src/packlets/diagnostics/browser-diagnostics-broadcast.d.ts +5 -0
  18. package/dist/types/src/packlets/diagnostics/browser-diagnostics-broadcast.d.ts.map +1 -0
  19. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts +5 -0
  20. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -0
  21. package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts +15 -0
  22. package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts.map +1 -0
  23. package/dist/types/src/packlets/{services → diagnostics}/diagnostics.d.ts +1 -1
  24. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -0
  25. package/dist/types/src/packlets/diagnostics/index.d.ts +4 -0
  26. package/dist/types/src/packlets/diagnostics/index.d.ts.map +1 -0
  27. package/dist/types/src/packlets/indexing/util.d.ts +2 -2
  28. package/dist/types/src/packlets/indexing/util.d.ts.map +1 -1
  29. package/dist/types/src/packlets/services/index.d.ts +1 -1
  30. package/dist/types/src/packlets/services/index.d.ts.map +1 -1
  31. package/dist/types/src/packlets/services/service-context.d.ts +7 -5
  32. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  33. package/dist/types/src/packlets/services/service-host.d.ts +2 -0
  34. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  35. package/dist/types/src/packlets/services/util.d.ts +1 -0
  36. package/dist/types/src/packlets/services/util.d.ts.map +1 -1
  37. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  38. package/dist/types/src/packlets/storage/index.d.ts +1 -0
  39. package/dist/types/src/packlets/storage/index.d.ts.map +1 -1
  40. package/dist/types/src/packlets/storage/level.d.ts +4 -0
  41. package/dist/types/src/packlets/storage/level.d.ts.map +1 -0
  42. package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
  43. package/dist/types/src/packlets/storage/util.d.ts +4 -0
  44. package/dist/types/src/packlets/storage/util.d.ts.map +1 -0
  45. package/dist/types/src/packlets/system/system-service.d.ts +1 -1
  46. package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
  47. package/dist/types/src/packlets/testing/test-builder.d.ts +1 -1
  48. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  49. package/dist/types/src/packlets/vault/shared-worker-connection.d.ts +5 -5
  50. package/dist/types/src/packlets/vault/shared-worker-connection.d.ts.map +1 -1
  51. package/dist/types/src/packlets/vault/worker-runtime.d.ts +2 -0
  52. package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
  53. package/dist/types/src/packlets/vault/worker-session.d.ts +2 -0
  54. package/dist/types/src/packlets/vault/worker-session.d.ts.map +1 -1
  55. package/dist/types/src/version.d.ts +1 -1
  56. package/dist/types/src/version.d.ts.map +1 -1
  57. package/package.json +36 -34
  58. package/src/index.ts +1 -0
  59. package/src/packlets/devices/devices-service.test.ts +1 -1
  60. package/src/packlets/diagnostics/browser-diagnostics-broadcast.ts +94 -0
  61. package/src/packlets/diagnostics/diagnostics-broadcast.ts +20 -0
  62. package/src/packlets/diagnostics/diagnostics-collector.ts +65 -0
  63. package/src/packlets/{services → diagnostics}/diagnostics.ts +2 -2
  64. package/src/packlets/diagnostics/index.ts +7 -0
  65. package/src/packlets/identity/identity-service.test.ts +1 -1
  66. package/src/packlets/indexing/util.ts +4 -4
  67. package/src/packlets/invitations/device-invitation-protocol.test.ts +1 -1
  68. package/src/packlets/network/network-service.test.ts +1 -1
  69. package/src/packlets/services/index.ts +1 -1
  70. package/src/packlets/services/service-context.test.ts +5 -5
  71. package/src/packlets/services/service-context.ts +13 -8
  72. package/src/packlets/services/service-host.ts +41 -9
  73. package/src/packlets/services/service-registry.test.ts +1 -1
  74. package/src/packlets/services/util.ts +2 -0
  75. package/src/packlets/spaces/data-space.ts +51 -2
  76. package/src/packlets/spaces/spaces-service.test.ts +1 -1
  77. package/src/packlets/storage/index.ts +1 -0
  78. package/src/packlets/storage/level.ts +19 -0
  79. package/src/packlets/storage/storage.ts +3 -9
  80. package/src/packlets/storage/util.ts +19 -0
  81. package/src/packlets/system/system-service.ts +1 -1
  82. package/src/packlets/testing/test-builder.ts +5 -3
  83. package/src/packlets/vault/shared-worker-connection.ts +3 -8
  84. package/src/packlets/vault/worker-runtime.ts +27 -2
  85. package/src/packlets/vault/worker-session.ts +6 -0
  86. package/src/version.ts +1 -5
  87. package/dist/lib/browser/chunk-RQ33OGAG.mjs.map +0 -7
  88. package/dist/lib/node/chunk-CBSM4HU4.cjs.map +0 -7
  89. package/dist/types/src/packlets/services/diagnostics.d.ts.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/client-services",
3
- "version": "0.4.9",
3
+ "version": "0.4.10-main.06ef97a",
4
4
  "description": "DXOS client services implementation",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -10,6 +10,7 @@
10
10
  "browser": {
11
11
  "jsondown": false,
12
12
  "./src/packlets/locks/node.ts": "./src/packlets/locks/browser.ts",
13
+ "./src/packlets/diagnostics/diagnostics-broadcast.ts": "./src/packlets/diagnostics/browser-diagnostics-broadcast.ts",
13
14
  "./dist/lib/node/index.cjs": "./dist/lib/browser/index.mjs",
14
15
  "./testing.js": "./dist/lib/browser/packlets/testing/index.mjs"
15
16
  },
@@ -21,44 +22,45 @@
21
22
  "src"
22
23
  ],
23
24
  "dependencies": {
25
+ "level": "^8.0.1",
24
26
  "platform": "^1.3.6",
25
- "@dxos/automerge": "0.4.9",
26
- "@dxos/client-protocol": "0.4.9",
27
- "@dxos/codec-protobuf": "0.4.9",
28
- "@dxos/context": "0.4.9",
29
- "@dxos/config": "0.4.9",
30
- "@dxos/credentials": "0.4.9",
31
- "@dxos/debug": "0.4.9",
32
- "@dxos/crypto": "0.4.9",
33
- "@dxos/echo-db": "0.4.9",
34
- "@dxos/echo-pipeline": "0.4.9",
35
- "@dxos/async": "0.4.9",
36
- "@dxos/feed-store": "0.4.9",
37
- "@dxos/echo-schema": "0.4.9",
38
- "@dxos/keyring": "0.4.9",
39
- "@dxos/invariant": "0.4.9",
40
- "@dxos/keys": "0.4.9",
41
- "@dxos/indexing": "0.4.9",
42
- "@dxos/log": "0.4.9",
43
- "@dxos/lock-file": "0.4.9",
44
- "@dxos/messaging": "0.4.9",
45
- "@dxos/network-manager": "0.4.9",
46
- "@dxos/protocols": "0.4.9",
47
- "@dxos/node-std": "0.4.9",
48
- "@dxos/random-access-storage": "0.4.9",
49
- "@dxos/rpc": "0.4.9",
50
- "@dxos/teleport": "0.4.9",
51
- "@dxos/teleport-extension-gossip": "0.4.9",
52
- "@dxos/teleport-extension-object-sync": "0.4.9",
53
- "@dxos/tracing": "0.4.9",
54
- "@dxos/util": "0.4.9",
55
- "@dxos/websocket-rpc": "0.4.9",
56
- "@dxos/timeframe": "0.4.9"
27
+ "@dxos/automerge": "0.4.10-main.06ef97a",
28
+ "@dxos/async": "0.4.10-main.06ef97a",
29
+ "@dxos/codec-protobuf": "0.4.10-main.06ef97a",
30
+ "@dxos/client-protocol": "0.4.10-main.06ef97a",
31
+ "@dxos/config": "0.4.10-main.06ef97a",
32
+ "@dxos/credentials": "0.4.10-main.06ef97a",
33
+ "@dxos/context": "0.4.10-main.06ef97a",
34
+ "@dxos/crypto": "0.4.10-main.06ef97a",
35
+ "@dxos/echo-db": "0.4.10-main.06ef97a",
36
+ "@dxos/debug": "0.4.10-main.06ef97a",
37
+ "@dxos/echo-pipeline": "0.4.10-main.06ef97a",
38
+ "@dxos/echo-schema": "0.4.10-main.06ef97a",
39
+ "@dxos/feed-store": "0.4.10-main.06ef97a",
40
+ "@dxos/invariant": "0.4.10-main.06ef97a",
41
+ "@dxos/indexing": "0.4.10-main.06ef97a",
42
+ "@dxos/keyring": "0.4.10-main.06ef97a",
43
+ "@dxos/lock-file": "0.4.10-main.06ef97a",
44
+ "@dxos/keys": "0.4.10-main.06ef97a",
45
+ "@dxos/log": "0.4.10-main.06ef97a",
46
+ "@dxos/messaging": "0.4.10-main.06ef97a",
47
+ "@dxos/protocols": "0.4.10-main.06ef97a",
48
+ "@dxos/network-manager": "0.4.10-main.06ef97a",
49
+ "@dxos/random-access-storage": "0.4.10-main.06ef97a",
50
+ "@dxos/node-std": "0.4.10-main.06ef97a",
51
+ "@dxos/rpc": "0.4.10-main.06ef97a",
52
+ "@dxos/teleport": "0.4.10-main.06ef97a",
53
+ "@dxos/teleport-extension-gossip": "0.4.10-main.06ef97a",
54
+ "@dxos/timeframe": "0.4.10-main.06ef97a",
55
+ "@dxos/teleport-extension-object-sync": "0.4.10-main.06ef97a",
56
+ "@dxos/tracing": "0.4.10-main.06ef97a",
57
+ "@dxos/util": "0.4.10-main.06ef97a",
58
+ "@dxos/websocket-rpc": "0.4.10-main.06ef97a"
57
59
  },
58
60
  "devDependencies": {
59
61
  "@types/platform": "^1.3.4",
60
62
  "@types/readable-stream": "^2.3.9",
61
- "@dxos/signal": "0.4.9"
63
+ "@dxos/signal": "0.4.10-main.06ef97a"
62
64
  },
63
65
  "publishConfig": {
64
66
  "access": "public"
package/src/index.ts CHANGED
@@ -10,3 +10,4 @@ export * from './packlets/spaces';
10
10
  export * from './packlets/storage';
11
11
  export * from './packlets/vault';
12
12
  export * from './packlets/locks';
13
+ export * from './packlets/diagnostics';
@@ -19,7 +19,7 @@ describe('DevicesService', () => {
19
19
  let devicesService: DevicesService;
20
20
 
21
21
  beforeEach(async () => {
22
- serviceContext = createServiceContext();
22
+ serviceContext = await createServiceContext();
23
23
  await serviceContext.open(new Context());
24
24
  devicesService = new DevicesServiceImpl(serviceContext.identityManager);
25
25
  });
@@ -0,0 +1,94 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { Trigger } from '@dxos/async';
6
+ import { log } from '@dxos/log';
7
+ import { type SystemService } from '@dxos/protocols/proto/dxos/client/services';
8
+
9
+ import {
10
+ type CollectDiagnosticsBroadcastSender,
11
+ type CollectDiagnosticsBroadcastHandler,
12
+ } from './diagnostics-collector';
13
+
14
+ const CHANNEL_NAME = 'dxos.diagnostics.broadcast';
15
+
16
+ enum MessageType {
17
+ PROBE = 'probe',
18
+ PROBE_ACK = 'probe-ack',
19
+ REQUEST_DIAGNOSTICS = 'request-diagnostics',
20
+ RECEIVE_DIAGNOSTICS = 'receive-diagnostics',
21
+ }
22
+
23
+ interface Message {
24
+ type: MessageType;
25
+ payload?: any;
26
+ }
27
+
28
+ export const createCollectDiagnosticsBroadcastSender = (): CollectDiagnosticsBroadcastSender => {
29
+ return {
30
+ broadcastDiagnosticsRequest: async () => {
31
+ let expectedResponse = MessageType.PROBE_ACK;
32
+ let channel: BroadcastChannel | undefined;
33
+ try {
34
+ const trigger = new Trigger<Message>();
35
+ channel = new BroadcastChannel(CHANNEL_NAME);
36
+ channel.onmessage = (msg) => {
37
+ if (expectedResponse === msg.data.type) {
38
+ trigger.wake(msg.data);
39
+ }
40
+ };
41
+ channel.postMessage({ type: MessageType.PROBE });
42
+ await trigger.wait({ timeout: 200 });
43
+ expectedResponse = MessageType.RECEIVE_DIAGNOSTICS;
44
+ trigger.reset();
45
+ channel.postMessage({ type: MessageType.REQUEST_DIAGNOSTICS });
46
+ const diagnostics = await trigger.wait({ timeout: 5000 });
47
+ return diagnostics.payload;
48
+ } catch (e) {
49
+ const errorDescription = e instanceof Error ? e.message : JSON.stringify(e);
50
+ return { expectedResponse, errorDescription };
51
+ } finally {
52
+ safeClose(channel);
53
+ }
54
+ },
55
+ };
56
+ };
57
+
58
+ export const createCollectDiagnosticsBroadcastHandler = (
59
+ systemService: SystemService,
60
+ ): CollectDiagnosticsBroadcastHandler => {
61
+ let channel: BroadcastChannel | undefined;
62
+ return {
63
+ start: () => {
64
+ channel = new BroadcastChannel(CHANNEL_NAME);
65
+ channel.onmessage = async (message) => {
66
+ try {
67
+ if (message.data.type === MessageType.PROBE) {
68
+ channel?.postMessage({ type: MessageType.PROBE_ACK });
69
+ } else if (message.data.type === MessageType.REQUEST_DIAGNOSTICS) {
70
+ const diagnostics = await systemService.getDiagnostics({});
71
+ channel?.postMessage({
72
+ type: MessageType.RECEIVE_DIAGNOSTICS,
73
+ payload: diagnostics,
74
+ });
75
+ }
76
+ } catch (error) {
77
+ log.catch(error);
78
+ }
79
+ };
80
+ },
81
+ stop: () => {
82
+ safeClose(channel);
83
+ channel = undefined;
84
+ },
85
+ };
86
+ };
87
+
88
+ const safeClose = (channel?: BroadcastChannel) => {
89
+ try {
90
+ channel?.close();
91
+ } catch (e) {
92
+ // ignored
93
+ }
94
+ };
@@ -0,0 +1,20 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+ import { type SystemService } from '@dxos/protocols/proto/dxos/client/services';
5
+
6
+ import {
7
+ type CollectDiagnosticsBroadcastSender,
8
+ type CollectDiagnosticsBroadcastHandler,
9
+ } from './diagnostics-collector';
10
+
11
+ export const createCollectDiagnosticsBroadcastSender = (): CollectDiagnosticsBroadcastSender => {
12
+ return { broadcastDiagnosticsRequest: async () => undefined };
13
+ };
14
+
15
+ export const createCollectDiagnosticsBroadcastHandler = (_: SystemService): CollectDiagnosticsBroadcastHandler => {
16
+ return {
17
+ start: () => {},
18
+ stop: () => {},
19
+ };
20
+ };
@@ -0,0 +1,65 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type ClientServicesProvider } from '@dxos/client-protocol';
6
+ import { type Config, ConfigResource } from '@dxos/config';
7
+ import { GetDiagnosticsRequest } from '@dxos/protocols/proto/dxos/client/services';
8
+ import { TRACE_PROCESSOR } from '@dxos/tracing';
9
+ import { type JsonKeyOptions, jsonKeyReplacer, nonNullable } from '@dxos/util';
10
+
11
+ import { createCollectDiagnosticsBroadcastSender } from './diagnostics-broadcast';
12
+ import { ClientServicesProviderResource } from '../services';
13
+
14
+ export interface CollectDiagnosticsBroadcastSender {
15
+ broadcastDiagnosticsRequest(): any;
16
+ }
17
+
18
+ export interface CollectDiagnosticsBroadcastHandler {
19
+ start(): void;
20
+ stop(): void;
21
+ }
22
+
23
+ export class DiagnosticsCollector {
24
+ private static broadcastSender = createCollectDiagnosticsBroadcastSender();
25
+
26
+ public static async collect(
27
+ config: Config | Config[] = findConfigs(),
28
+ services: ClientServicesProvider | null = findSystemServiceProvider(),
29
+ options: JsonKeyOptions = {},
30
+ ): Promise<any> {
31
+ const serviceDiagnostics = await services?.services?.SystemService?.getDiagnostics({
32
+ keys: options.humanize
33
+ ? GetDiagnosticsRequest.KEY_OPTION.HUMANIZE
34
+ : options.truncate
35
+ ? GetDiagnosticsRequest.KEY_OPTION.TRUNCATE
36
+ : undefined,
37
+ });
38
+
39
+ const clientDiagnostics = {
40
+ config,
41
+ trace: TRACE_PROCESSOR.getDiagnostics(),
42
+ };
43
+
44
+ const diagnostics =
45
+ serviceDiagnostics != null
46
+ ? { client: clientDiagnostics, services: serviceDiagnostics }
47
+ : {
48
+ client: clientDiagnostics,
49
+ broadcast: await this.broadcastSender.broadcastDiagnosticsRequest(),
50
+ };
51
+
52
+ return JSON.parse(JSON.stringify(diagnostics, jsonKeyReplacer(options)));
53
+ }
54
+ }
55
+
56
+ const findSystemServiceProvider = (): ClientServicesProvider | null => {
57
+ const serviceProviders = TRACE_PROCESSOR.findByAnnotation(ClientServicesProviderResource);
58
+ const providerResource = serviceProviders.find((r) => r.instance.deref()?.services?.SystemService != null);
59
+ return providerResource?.instance?.deref() ?? null;
60
+ };
61
+
62
+ const findConfigs = (): Config[] => {
63
+ const configs = TRACE_PROCESSOR.findByAnnotation(ConfigResource);
64
+ return configs.map((r) => r.instance.deref()).filter(nonNullable);
65
+ };
@@ -25,9 +25,9 @@ import { type Epoch } from '@dxos/protocols/proto/dxos/halo/credentials';
25
25
  import { type Resource, type Span } from '@dxos/protocols/proto/dxos/tracing';
26
26
  import { TRACE_PROCESSOR } from '@dxos/tracing';
27
27
 
28
- import { getPlatform } from './platform';
29
- import { type ServiceContext } from './service-context';
30
28
  import { DXOS_VERSION } from '../../version';
29
+ import { type ServiceContext } from '../services';
30
+ import { getPlatform } from '../services/platform';
31
31
  import { type DataSpace } from '../spaces';
32
32
 
33
33
  const DEFAULT_TIMEOUT = 1_000;
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './diagnostics';
6
+ export * from './diagnostics-collector';
7
+ export * from './diagnostics-broadcast';
@@ -22,7 +22,7 @@ describe('IdentityService', () => {
22
22
  let identityService: IdentityService;
23
23
 
24
24
  beforeEach(async () => {
25
- serviceContext = createServiceContext();
25
+ serviceContext = await createServiceContext();
26
26
  await serviceContext.open(new Context());
27
27
  identityService = new IdentityServiceImpl(
28
28
  (options) => serviceContext.createIdentity(options),
@@ -12,7 +12,7 @@ import { idCodec } from '@dxos/protocols';
12
12
  /**
13
13
  * Factory for `loadDocuments` iterator.
14
14
  */
15
- export const createLoadDocuments = (automergeHost: AutomergeHost) =>
15
+ export const createSelectedDocumentsIterator = (automergeHost: AutomergeHost) =>
16
16
  /**
17
17
  * Get object data blobs from Automerge Repo by ids.
18
18
  * @param ids
@@ -25,14 +25,14 @@ export const createLoadDocuments = (automergeHost: AutomergeHost) =>
25
25
  await warnAfterTimeout(5000, 'to long to load doc', () => handle.whenReady());
26
26
  const doc = handle.docSync();
27
27
  const hash = getHeads(doc).join('');
28
- yield [{ id, object: doc.objects[objectId], currentHash: hash }];
28
+ yield doc.objects?.[objectId] ? [{ id, object: doc.objects[objectId], currentHash: hash }] : [];
29
29
  }
30
30
  };
31
31
 
32
32
  /**
33
33
  * Factory for `getAllDocuments` iterator.
34
34
  */
35
- export const createGetAllDocuments = (automergeHost: AutomergeHost) =>
35
+ export const createDocumentsIterator = (automergeHost: AutomergeHost) =>
36
36
  /**
37
37
  * Recursively get all object data blobs from Automerge Repo.
38
38
  * @param ids
@@ -57,7 +57,7 @@ export const createGetAllDocuments = (automergeHost: AutomergeHost) =>
57
57
  return {
58
58
  id: idCodec.encode({ documentId: handle.documentId, objectId }),
59
59
  object,
60
- currentHash: heads.at(-1)!,
60
+ currentHash: heads.join(''),
61
61
  };
62
62
  });
63
63
  }
@@ -20,7 +20,7 @@ const closeAfterTest = async (peer: ServiceContext) => {
20
20
 
21
21
  describe('services/device', () => {
22
22
  test('creates identity', async () => {
23
- const peer = createServiceContext();
23
+ const peer = await createServiceContext();
24
24
  await peer.open(new Context());
25
25
  afterTest(() => peer.close());
26
26
 
@@ -18,7 +18,7 @@ describe('NetworkService', () => {
18
18
  let networkService: NetworkService;
19
19
 
20
20
  beforeEach(async () => {
21
- serviceContext = createServiceContext();
21
+ serviceContext = await createServiceContext();
22
22
  await serviceContext.open(new Context());
23
23
  networkService = new NetworkServiceImpl(serviceContext.networkManager, serviceContext.signalManager);
24
24
  });
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  export * from './client-rpc-server';
6
- export * from './diagnostics';
7
6
  export * from './service-context';
8
7
  export * from './service-host';
9
8
  export * from './service-registry';
9
+ export { ClientServicesProviderResource } from './util';
@@ -12,10 +12,10 @@ import { performInvitation } from '../testing/invitation-utils';
12
12
  describe('services/ServiceContext', () => {
13
13
  test('new space is synchronized on device invitations', async () => {
14
14
  const networkContext = new MemorySignalManagerContext();
15
- const device1 = createServiceContext({ signalContext: networkContext });
15
+ const device1 = await createServiceContext({ signalContext: networkContext });
16
16
  await device1.createIdentity();
17
17
 
18
- const device2 = createServiceContext({ signalContext: networkContext });
18
+ const device2 = await createServiceContext({ signalContext: networkContext });
19
19
  await Promise.all(performInvitation({ host: device1, guest: device2, options: { kind: Invitation.Kind.DEVICE } }));
20
20
 
21
21
  const space1 = await device1.dataSpaceManager!.createSpace();
@@ -26,13 +26,13 @@ describe('services/ServiceContext', () => {
26
26
 
27
27
  test('joined space is synchronized on device invitations', async () => {
28
28
  const networkContext = new MemorySignalManagerContext();
29
- const device1 = createServiceContext({ signalContext: networkContext });
29
+ const device1 = await createServiceContext({ signalContext: networkContext });
30
30
  await device1.createIdentity();
31
31
 
32
- const device2 = createServiceContext({ signalContext: networkContext });
32
+ const device2 = await createServiceContext({ signalContext: networkContext });
33
33
  await Promise.all(performInvitation({ host: device1, guest: device2, options: { kind: Invitation.Kind.DEVICE } }));
34
34
 
35
- const identity2 = createServiceContext({ signalContext: networkContext });
35
+ const identity2 = await createServiceContext({ signalContext: networkContext });
36
36
  await identity2.createIdentity();
37
37
  const space1 = await identity2.dataSpaceManager!.createSpace();
38
38
  await Promise.all(
@@ -2,8 +2,10 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
+ import { type Level } from 'level';
6
+
5
7
  import { Trigger } from '@dxos/async';
6
- import { Context } from '@dxos/context';
8
+ import { Context, Resource } from '@dxos/context';
7
9
  import { getCredentialAssertion, type CredentialProcessor } from '@dxos/credentials';
8
10
  import { failUndefined } from '@dxos/debug';
9
11
  import { AutomergeHost, MetadataStore, SnapshotStore, SpaceManager, valueEncoding } from '@dxos/echo-pipeline';
@@ -30,7 +32,7 @@ import {
30
32
  type IdentityManagerRuntimeParams,
31
33
  type JoinIdentityParams,
32
34
  } from '../identity';
33
- import { createGetAllDocuments, createLoadDocuments } from '../indexing';
35
+ import { createDocumentsIterator, createSelectedDocumentsIterator } from '../indexing';
34
36
  import {
35
37
  DeviceInvitationProtocol,
36
38
  InvitationsHandler,
@@ -47,7 +49,7 @@ export type ServiceContextRuntimeParams = IdentityManagerRuntimeParams & DataSpa
47
49
  // TODO(dmaretskyi): Gets duplicated in CJS build between normal and testing bundles.
48
50
  @safeInstanceof('dxos.client-services.ServiceContext')
49
51
  @Trace.resource()
50
- export class ServiceContext {
52
+ export class ServiceContext extends Resource {
51
53
  public readonly initialized = new Trigger();
52
54
  public readonly metadataStore: MetadataStore;
53
55
  /**
@@ -78,10 +80,13 @@ export class ServiceContext {
78
80
 
79
81
  constructor(
80
82
  public readonly storage: Storage,
83
+ public readonly level: Level<string, string>,
81
84
  public readonly networkManager: NetworkManager,
82
85
  public readonly signalManager: SignalManager,
83
86
  public readonly _runtimeParams?: IdentityManagerRuntimeParams & DataSpaceManagerRuntimeParams,
84
87
  ) {
88
+ super();
89
+
85
90
  // TODO(burdon): Move strings to constants.
86
91
  this.metadataStore = new MetadataStore(storage.createDirectory('metadata'));
87
92
  this.snapshotStore = new SnapshotStore(storage.createDirectory('snapshots'));
@@ -115,7 +120,7 @@ export class ServiceContext {
115
120
  this._runtimeParams as IdentityManagerRuntimeParams,
116
121
  );
117
122
 
118
- this.indexMetadata = new IndexMetadataStore({ directory: storage.createDirectory('index-metadata') });
123
+ this.indexMetadata = new IndexMetadataStore({ db: level.sublevel('index-metadata') });
119
124
 
120
125
  this.automergeHost = new AutomergeHost({
121
126
  directory: storage.createDirectory('automerge'),
@@ -125,8 +130,8 @@ export class ServiceContext {
125
130
  this.indexer = new Indexer({
126
131
  indexStore: new IndexStore({ directory: storage.createDirectory('index-store') }),
127
132
  metadataStore: this.indexMetadata,
128
- loadDocuments: createLoadDocuments(this.automergeHost),
129
- getAllDocuments: createGetAllDocuments(this.automergeHost),
133
+ loadDocuments: createSelectedDocumentsIterator(this.automergeHost),
134
+ getAllDocuments: createDocumentsIterator(this.automergeHost),
130
135
  });
131
136
 
132
137
  this.invitations = new InvitationsHandler(this.networkManager);
@@ -145,7 +150,7 @@ export class ServiceContext {
145
150
  }
146
151
 
147
152
  @Trace.span()
148
- async open(ctx: Context) {
153
+ protected override async _open(ctx: Context) {
149
154
  await this._checkStorageVersion();
150
155
 
151
156
  log('opening...');
@@ -163,7 +168,7 @@ export class ServiceContext {
163
168
  log('opened');
164
169
  }
165
170
 
166
- async close() {
171
+ protected override async _close() {
167
172
  log('closing...');
168
173
  if (this._deviceSpaceSync && this.identityManager.identity) {
169
174
  await this.identityManager.identity.space.spaceState.removeCredentialProcessor(this._deviceSpaceSync);
@@ -2,12 +2,14 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
+ import { type Level } from 'level';
6
+
5
7
  import { Event, synchronized } from '@dxos/async';
6
- import { Properties, clientServiceBundle, defaultKey, type ClientServices } from '@dxos/client-protocol';
8
+ import { clientServiceBundle, defaultKey, type ClientServices, Properties } from '@dxos/client-protocol';
7
9
  import { type Config } from '@dxos/config';
8
10
  import { Context } from '@dxos/context';
9
- import { DataServiceImpl } from '@dxos/echo-pipeline';
10
- import { getAutomergeObjectCore, getRawDoc, type SpaceDoc, type TypedObject } from '@dxos/echo-schema';
11
+ import { DataServiceImpl, type ObjectStructure, encodeReference, type SpaceDoc } from '@dxos/echo-pipeline';
12
+ import * as E from '@dxos/echo-schema';
11
13
  import { IndexServiceImpl } from '@dxos/indexing';
12
14
  import { invariant } from '@dxos/invariant';
13
15
  import { PublicKey } from '@dxos/keys';
@@ -21,18 +23,22 @@ import { TRACE_PROCESSOR, trace as Trace } from '@dxos/tracing';
21
23
  import { assignDeep } from '@dxos/util';
22
24
  import { WebsocketRpcClient } from '@dxos/websocket-rpc';
23
25
 
24
- import { createDiagnostics } from './diagnostics';
25
26
  import { ServiceContext, type ServiceContextRuntimeParams } from './service-context';
26
27
  import { ServiceRegistry } from './service-registry';
27
28
  import { DevicesServiceImpl } from '../devices';
28
29
  import { DevtoolsHostEvents, DevtoolsServiceImpl } from '../devtools';
30
+ import {
31
+ type CollectDiagnosticsBroadcastHandler,
32
+ createCollectDiagnosticsBroadcastHandler,
33
+ createDiagnostics,
34
+ } from '../diagnostics';
29
35
  import { IdentityServiceImpl, type CreateIdentityOptions } from '../identity';
30
36
  import { InvitationsServiceImpl } from '../invitations';
31
37
  import { Lock, type ResourceLock } from '../locks';
32
38
  import { LoggingServiceImpl } from '../logging';
33
39
  import { NetworkServiceImpl } from '../network';
34
40
  import { SpacesServiceImpl } from '../spaces';
35
- import { createStorageObjects } from '../storage';
41
+ import { createLevel, createStorageObjects } from '../storage';
36
42
  import { SystemServiceImpl } from '../system';
37
43
 
38
44
  export type ClientServicesHostParams = {
@@ -76,11 +82,13 @@ export class ClientServicesHost {
76
82
  private _signalManager?: SignalManager;
77
83
  private _networkManager?: NetworkManager;
78
84
  private _storage?: Storage;
85
+ private _level?: Level<string, string>;
79
86
  private _callbacks?: ClientServicesHostCallbacks;
80
87
  private _devtoolsProxy?: WebsocketRpcClient<{}, ClientServices>;
81
88
 
82
89
  private _serviceContext!: ServiceContext;
83
90
  private readonly _runtimeParams?: ServiceContextRuntimeParams;
91
+ private diagnosticsBroadcastHandler: CollectDiagnosticsBroadcastHandler;
84
92
 
85
93
  @Trace.info()
86
94
  private _opening = false;
@@ -138,6 +146,7 @@ export class ClientServicesHost {
138
146
  },
139
147
  });
140
148
 
149
+ this.diagnosticsBroadcastHandler = createCollectDiagnosticsBroadcastHandler(this._systemService);
141
150
  this._loggingService = new LoggingServiceImpl();
142
151
 
143
152
  this._serviceRegistry = new ServiceRegistry<ClientServices>(clientServiceBundle, {
@@ -187,6 +196,9 @@ export class ClientServicesHost {
187
196
  }
188
197
  }
189
198
 
199
+ if (!options.signalManager) {
200
+ log.warn('running signaling without telemetry metadata.');
201
+ }
190
202
  const {
191
203
  connectionLog = true,
192
204
  transportFactory = createSimplePeerTransportFactory({
@@ -223,12 +235,17 @@ export class ClientServicesHost {
223
235
 
224
236
  this._opening = true;
225
237
  log('opening...', { lockKey: this._resourceLock?.lockKey });
238
+
239
+ if (!this._level) {
240
+ this._level = await createLevel(this._config.get('runtime.client.storage', {})!);
241
+ }
226
242
  await this._resourceLock?.acquire();
227
243
 
228
244
  await this._loggingService.open();
229
245
 
230
246
  this._serviceContext = new ServiceContext(
231
247
  this._storage,
248
+ this._level,
232
249
  this._networkManager,
233
250
  this._signalManager,
234
251
  this._runtimeParams,
@@ -298,6 +315,7 @@ export class ClientServicesHost {
298
315
  });
299
316
  void this._devtoolsProxy.open();
300
317
  }
318
+ this.diagnosticsBroadcastHandler.start();
301
319
 
302
320
  this._opening = false;
303
321
  this._open = true;
@@ -316,10 +334,12 @@ export class ClientServicesHost {
316
334
 
317
335
  const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
318
336
  log('closing...', { deviceKey });
337
+ this.diagnosticsBroadcastHandler.stop();
319
338
  await this._devtoolsProxy?.close();
320
339
  this._serviceRegistry.setServices({ SystemService: this._systemService });
321
340
  await this._loggingService.close();
322
341
  await this._serviceContext.close();
342
+ await this._level?.close();
323
343
  this._open = false;
324
344
  this._statusUpdate.emit();
325
345
  log('closed', { deviceKey });
@@ -344,18 +364,30 @@ export class ClientServicesHost {
344
364
  await this._serviceContext.initialized.wait();
345
365
  const space = await this._serviceContext.dataSpaceManager!.createSpace();
346
366
 
347
- const obj: TypedObject = new Properties(undefined);
348
- obj[defaultKey] = identity.identityKey.toHex();
349
-
350
367
  const automergeIndex = space.automergeSpaceState.rootUrl;
351
368
  invariant(automergeIndex);
352
369
  const document = await this._serviceContext.automergeHost.repo.find<SpaceDoc>(automergeIndex as any);
353
370
  await document.whenReady();
354
371
 
372
+ // TODO(dmaretskyi): Better API for low-level data access.
373
+ const properties: ObjectStructure = {
374
+ system: {
375
+ type: encodeReference(E.getTypeReference(Properties)!),
376
+ },
377
+ data: {
378
+ [defaultKey]: identity.identityKey.toHex(),
379
+ },
380
+ meta: {
381
+ keys: [],
382
+ },
383
+ };
384
+ const propertiesId = PublicKey.random().toHex();
355
385
  document.change((doc: SpaceDoc) => {
356
- assignDeep(doc, ['objects', getAutomergeObjectCore(obj).id], getRawDoc(obj).handle.docSync());
386
+ assignDeep(doc, ['objects', propertiesId], properties);
357
387
  });
358
388
 
389
+ await this._serviceContext.automergeHost.repo.flush();
390
+
359
391
  return identity;
360
392
  }
361
393
  }
@@ -31,7 +31,7 @@ const serviceBundle = createServiceBundle<TestServices>({
31
31
  describe('service registry', () => {
32
32
  test('builds a service registry', async () => {
33
33
  const remoteSource = 'https://remote.source';
34
- const serviceContext = createServiceContext();
34
+ const serviceContext = await createServiceContext();
35
35
  await serviceContext.open(new Context());
36
36
 
37
37
  const serviceRegistry = new ServiceRegistry(serviceBundle, {