@dxos/client-services 0.4.10-main.c32f430 → 0.4.10-main.c42bfdb

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 (75) hide show
  1. package/dist/lib/browser/{chunk-FPOYSPWD.mjs → chunk-W7UANCHR.mjs} +697 -510
  2. package/dist/lib/browser/chunk-W7UANCHR.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +11 -1
  4. package/dist/lib/browser/index.mjs.map +1 -1
  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-ZILMQKKB.cjs → chunk-JSVLZGJM.cjs} +631 -531
  9. package/dist/lib/node/chunk-JSVLZGJM.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +47 -37
  11. package/dist/lib/node/index.cjs.map +1 -1
  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/services/index.d.ts +1 -1
  28. package/dist/types/src/packlets/services/index.d.ts.map +1 -1
  29. package/dist/types/src/packlets/services/service-context.d.ts +7 -5
  30. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  31. package/dist/types/src/packlets/services/service-host.d.ts +2 -0
  32. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  33. package/dist/types/src/packlets/services/util.d.ts +1 -0
  34. package/dist/types/src/packlets/services/util.d.ts.map +1 -1
  35. package/dist/types/src/packlets/storage/index.d.ts +1 -0
  36. package/dist/types/src/packlets/storage/index.d.ts.map +1 -1
  37. package/dist/types/src/packlets/storage/level.d.ts +4 -0
  38. package/dist/types/src/packlets/storage/level.d.ts.map +1 -0
  39. package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
  40. package/dist/types/src/packlets/storage/util.d.ts +4 -0
  41. package/dist/types/src/packlets/storage/util.d.ts.map +1 -0
  42. package/dist/types/src/packlets/system/system-service.d.ts +1 -1
  43. package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
  44. package/dist/types/src/packlets/testing/test-builder.d.ts +1 -1
  45. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  46. package/dist/types/src/version.d.ts +1 -1
  47. package/package.json +36 -34
  48. package/src/index.ts +1 -0
  49. package/src/packlets/devices/devices-service.test.ts +1 -1
  50. package/src/packlets/diagnostics/browser-diagnostics-broadcast.ts +94 -0
  51. package/src/packlets/diagnostics/diagnostics-broadcast.ts +20 -0
  52. package/src/packlets/diagnostics/diagnostics-collector.ts +65 -0
  53. package/src/packlets/{services → diagnostics}/diagnostics.ts +2 -2
  54. package/src/packlets/diagnostics/index.ts +7 -0
  55. package/src/packlets/identity/identity-service.test.ts +1 -1
  56. package/src/packlets/indexing/util.ts +1 -1
  57. package/src/packlets/invitations/device-invitation-protocol.test.ts +1 -1
  58. package/src/packlets/network/network-service.test.ts +1 -1
  59. package/src/packlets/services/index.ts +1 -1
  60. package/src/packlets/services/service-context.test.ts +5 -5
  61. package/src/packlets/services/service-context.ts +10 -5
  62. package/src/packlets/services/service-host.ts +38 -9
  63. package/src/packlets/services/service-registry.test.ts +1 -1
  64. package/src/packlets/services/util.ts +2 -0
  65. package/src/packlets/spaces/spaces-service.test.ts +1 -1
  66. package/src/packlets/storage/index.ts +1 -0
  67. package/src/packlets/storage/level.ts +19 -0
  68. package/src/packlets/storage/storage.ts +3 -9
  69. package/src/packlets/storage/util.ts +19 -0
  70. package/src/packlets/system/system-service.ts +1 -1
  71. package/src/packlets/testing/test-builder.ts +5 -3
  72. package/src/version.ts +1 -1
  73. package/dist/lib/browser/chunk-FPOYSPWD.mjs.map +0 -7
  74. package/dist/lib/node/chunk-ZILMQKKB.cjs.map +0 -7
  75. package/dist/types/src/packlets/services/diagnostics.d.ts.map +0 -1
@@ -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),
@@ -25,7 +25,7 @@ export const createSelectedDocumentsIterator = (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
 
@@ -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';
@@ -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'),
@@ -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, type SpaceDoc } from '@dxos/echo-pipeline';
10
- import { getAutomergeObjectCore, getRawDoc, 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, {
@@ -226,12 +235,17 @@ export class ClientServicesHost {
226
235
 
227
236
  this._opening = true;
228
237
  log('opening...', { lockKey: this._resourceLock?.lockKey });
238
+
239
+ if (!this._level) {
240
+ this._level = await createLevel(this._config.get('runtime.client.storage', {})!);
241
+ }
229
242
  await this._resourceLock?.acquire();
230
243
 
231
244
  await this._loggingService.open();
232
245
 
233
246
  this._serviceContext = new ServiceContext(
234
247
  this._storage,
248
+ this._level,
235
249
  this._networkManager,
236
250
  this._signalManager,
237
251
  this._runtimeParams,
@@ -301,6 +315,7 @@ export class ClientServicesHost {
301
315
  });
302
316
  void this._devtoolsProxy.open();
303
317
  }
318
+ this.diagnosticsBroadcastHandler.start();
304
319
 
305
320
  this._opening = false;
306
321
  this._open = true;
@@ -319,10 +334,12 @@ export class ClientServicesHost {
319
334
 
320
335
  const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
321
336
  log('closing...', { deviceKey });
337
+ this.diagnosticsBroadcastHandler.stop();
322
338
  await this._devtoolsProxy?.close();
323
339
  this._serviceRegistry.setServices({ SystemService: this._systemService });
324
340
  await this._loggingService.close();
325
341
  await this._serviceContext.close();
342
+ await this._level?.close();
326
343
  this._open = false;
327
344
  this._statusUpdate.emit();
328
345
  log('closed', { deviceKey });
@@ -347,18 +364,30 @@ export class ClientServicesHost {
347
364
  await this._serviceContext.initialized.wait();
348
365
  const space = await this._serviceContext.dataSpaceManager!.createSpace();
349
366
 
350
- const obj: TypedObject = new Properties(undefined);
351
- obj[defaultKey] = identity.identityKey.toHex();
352
-
353
367
  const automergeIndex = space.automergeSpaceState.rootUrl;
354
368
  invariant(automergeIndex);
355
369
  const document = await this._serviceContext.automergeHost.repo.find<SpaceDoc>(automergeIndex as any);
356
370
  await document.whenReady();
357
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();
358
385
  document.change((doc: SpaceDoc) => {
359
- assignDeep(doc, ['objects', getAutomergeObjectCore(obj).id], getRawDoc(obj).handle.docSync());
386
+ assignDeep(doc, ['objects', propertiesId], properties);
360
387
  });
361
388
 
389
+ await this._serviceContext.automergeHost.repo.flush();
390
+
362
391
  return identity;
363
392
  }
364
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, {
@@ -5,6 +5,8 @@
5
5
  import { PublicKey } from '@dxos/keys';
6
6
  import { humanize } from '@dxos/util';
7
7
 
8
+ export const ClientServicesProviderResource = Symbol.for('dxos.resource.ClientServices');
9
+
8
10
  // TODO(burdon): Move to util.
9
11
 
10
12
  export type JsonStringifyOptions = {
@@ -22,7 +22,7 @@ describe('SpacesService', () => {
22
22
  let spacesService: SpacesService;
23
23
 
24
24
  beforeEach(async () => {
25
- serviceContext = createServiceContext();
25
+ serviceContext = await createServiceContext();
26
26
  await serviceContext.open(new Context());
27
27
  spacesService = new SpacesServiceImpl(serviceContext.identityManager, serviceContext.spaceManager, async () => {
28
28
  await serviceContext.initialized.wait();
@@ -3,3 +3,4 @@
3
3
  //
4
4
 
5
5
  export * from './storage';
6
+ export * from './level';
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { Level } from 'level';
6
+ import path from 'node:path';
7
+
8
+ import { PublicKey } from '@dxos/keys';
9
+ import { type Runtime } from '@dxos/protocols/proto/dxos/config';
10
+
11
+ import { getRootPath, isPersistent } from './util';
12
+
13
+ export const createLevel = async (config: Runtime.Client.Storage) => {
14
+ const persistent = isPersistent(config);
15
+ const storagePath = persistent ? getRootPath(config) : path.join('tmp', 'level', PublicKey.random().toHex());
16
+ const level = new Level<string, string>(storagePath);
17
+ await level.open();
18
+ return level;
19
+ };
@@ -4,22 +4,16 @@
4
4
  // Copyright 2023 DXOS.org
5
5
  //
6
6
 
7
- import { DX_DATA } from '@dxos/client-protocol';
8
7
  import { InvalidConfigError } from '@dxos/protocols';
9
8
  import { Runtime } from '@dxos/protocols/proto/dxos/config';
10
9
  import { createStorage, StorageType } from '@dxos/random-access-storage';
11
- import { isNode } from '@dxos/util';
12
10
 
13
11
  import StorageDriver = Runtime.Client.Storage.StorageDriver;
12
+ import { getRootPath } from './util';
14
13
 
15
14
  // TODO(burdon): Factor out.
16
15
  export const createStorageObjects = (config: Runtime.Client.Storage) => {
17
- const {
18
- persistent = false,
19
- keyStore,
20
- dataStore,
21
- dataRoot = isNode() ? DX_DATA : 'dxos/storage', // TODO(burdon): Factor out const.
22
- } = config ?? {};
16
+ const { persistent = false, keyStore, dataStore } = config ?? {};
23
17
 
24
18
  if (persistent && dataStore === StorageDriver.RAM) {
25
19
  throw new InvalidConfigError('RAM storage cannot be used in persistent mode.');
@@ -37,7 +31,7 @@ export const createStorageObjects = (config: Runtime.Client.Storage) => {
37
31
  return {
38
32
  storage: createStorage({
39
33
  type: persistent ? toStorageType(dataStore) : StorageType.RAM,
40
- root: `${dataRoot}/`,
34
+ root: getRootPath(config),
41
35
  }),
42
36
  };
43
37
  };
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { DX_DATA } from '@dxos/client-protocol';
6
+ import { Runtime } from '@dxos/protocols/proto/dxos/config';
7
+ import { isNode } from '@dxos/util';
8
+
9
+ export const getRootPath = (config: Runtime.Client.Storage) => {
10
+ const { dataRoot = isNode() ? DX_DATA : 'dxos/storage' } = config ?? {};
11
+ return `${dataRoot}/`;
12
+ };
13
+
14
+ export const isPersistent = (config: Runtime.Client.Storage) => {
15
+ const { persistent = false } = config ?? {};
16
+ return (
17
+ (config.dataStore !== undefined && config.dataStore !== Runtime.Client.Storage.StorageDriver.RAM) || persistent
18
+ );
19
+ };
@@ -16,7 +16,7 @@ import {
16
16
  } from '@dxos/protocols/proto/dxos/client/services';
17
17
  import { jsonKeyReplacer, type MaybePromise } from '@dxos/util';
18
18
 
19
- import { type Diagnostics } from '../services';
19
+ import { type Diagnostics } from '../diagnostics';
20
20
  import { getPlatform } from '../services/platform';
21
21
 
22
22
  export type SystemServiceOptions = {
@@ -8,6 +8,7 @@ import { createCredentialSignerWithChain, CredentialGenerator } from '@dxos/cred
8
8
  import { failUndefined } from '@dxos/debug';
9
9
  import { AutomergeHost, MetadataStore, SnapshotStore, SpaceManager, valueEncoding } from '@dxos/echo-pipeline';
10
10
  import { FeedFactory, FeedStore } from '@dxos/feed-store';
11
+ import { createTestLevel } from '@dxos/indexing/testing';
11
12
  import { Keyring } from '@dxos/keyring';
12
13
  import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
13
14
  import { MemoryTransportFactory, NetworkManager } from '@dxos/network-manager';
@@ -29,7 +30,7 @@ export const createServiceHost = (config: Config, signalManagerContext: MemorySi
29
30
  });
30
31
  };
31
32
 
32
- export const createServiceContext = ({
33
+ export const createServiceContext = async ({
33
34
  signalContext = new MemorySignalManagerContext(),
34
35
  storage = createStorage({ type: StorageType.RAM }),
35
36
  }: {
@@ -41,8 +42,9 @@ export const createServiceContext = ({
41
42
  signalManager,
42
43
  transportFactory: MemoryTransportFactory,
43
44
  });
45
+ const level = await createTestLevel();
44
46
 
45
- return new ServiceContext(storage, networkManager, signalManager);
47
+ return new ServiceContext(storage, level, networkManager, signalManager);
46
48
  };
47
49
 
48
50
  export const createPeers = async (numPeers: number) => {
@@ -50,7 +52,7 @@ export const createPeers = async (numPeers: number) => {
50
52
 
51
53
  return await Promise.all(
52
54
  Array.from(Array(numPeers)).map(async () => {
53
- const peer = createServiceContext({ signalContext });
55
+ const peer = await createServiceContext({ signalContext });
54
56
  await peer.open(new Context());
55
57
  return peer;
56
58
  }),
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const DXOS_VERSION = "0.4.10-main.c32f430";
1
+ export const DXOS_VERSION = "0.4.10-main.c42bfdb";