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

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 (50) hide show
  1. package/dist/lib/browser/{chunk-W7UANCHR.mjs → chunk-JP7F2IH3.mjs} +513 -403
  2. package/dist/lib/browser/chunk-JP7F2IH3.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +5 -3
  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 +9 -4
  7. package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/{chunk-JSVLZGJM.cjs → chunk-34EZSH65.cjs} +520 -411
  9. package/dist/lib/node/chunk-34EZSH65.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +45 -43
  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 +14 -9
  14. package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
  15. package/dist/types/src/packlets/indexing/util.d.ts +2 -1
  16. package/dist/types/src/packlets/indexing/util.d.ts.map +1 -1
  17. package/dist/types/src/packlets/invitations/index.d.ts +1 -0
  18. package/dist/types/src/packlets/invitations/index.d.ts.map +1 -1
  19. package/dist/types/src/packlets/invitations/invitation-extension.d.ts +1 -0
  20. package/dist/types/src/packlets/invitations/invitation-extension.d.ts.map +1 -1
  21. package/dist/types/src/packlets/invitations/invitations-handler.d.ts +4 -2
  22. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  23. package/dist/types/src/packlets/invitations/invitations-manager.d.ts +42 -0
  24. package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -0
  25. package/dist/types/src/packlets/invitations/invitations-service.d.ts +7 -23
  26. package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
  27. package/dist/types/src/packlets/services/service-context.d.ts +2 -0
  28. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  29. package/dist/types/src/packlets/services/service-host.d.ts +3 -1
  30. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  31. package/dist/types/src/packlets/testing/test-builder.d.ts +3 -1
  32. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  33. package/dist/types/src/version.d.ts +1 -1
  34. package/package.json +34 -34
  35. package/src/packlets/indexing/util.ts +2 -2
  36. package/src/packlets/invitations/index.ts +1 -0
  37. package/src/packlets/invitations/invitation-extension.ts +28 -1
  38. package/src/packlets/invitations/invitations-handler.ts +74 -34
  39. package/src/packlets/invitations/invitations-manager.ts +197 -0
  40. package/src/packlets/invitations/invitations-service.ts +21 -168
  41. package/src/packlets/services/automerge-host.test.ts +9 -3
  42. package/src/packlets/services/service-context.test.ts +4 -1
  43. package/src/packlets/services/service-context.ts +16 -3
  44. package/src/packlets/services/service-host.ts +15 -13
  45. package/src/packlets/spaces/data-space-manager.test.ts +4 -4
  46. package/src/packlets/storage/level.ts +1 -1
  47. package/src/packlets/testing/test-builder.ts +20 -4
  48. package/src/version.ts +1 -1
  49. package/dist/lib/browser/chunk-W7UANCHR.mjs.map +0 -7
  50. package/dist/lib/node/chunk-JSVLZGJM.cjs.map +0 -7
@@ -2,43 +2,22 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { Event, scheduleTask } from '@dxos/async';
6
- import { type AuthenticatingInvitation, type CancellableInvitation } from '@dxos/client-protocol';
7
5
  import { Stream } from '@dxos/codec-protobuf';
8
- import { Context } from '@dxos/context';
9
- import { type MetadataStore } from '@dxos/echo-pipeline';
10
- import { invariant } from '@dxos/invariant';
11
- import { log } from '@dxos/log';
12
6
  import {
13
7
  type AuthenticationRequest,
14
8
  type AcceptInvitationRequest,
15
- Invitation,
9
+ type Invitation,
16
10
  type InvitationsService,
17
11
  QueryInvitationsResponse,
18
12
  } from '@dxos/protocols/proto/dxos/client/services';
19
13
 
20
- import { type InvitationProtocol } from './invitation-protocol';
21
- import { invitationExpired, type InvitationsHandler } from './invitations-handler';
14
+ import { type InvitationsManager } from './invitations-manager';
22
15
 
23
16
  /**
24
17
  * Adapts invitation service observable to client/service stream.
25
18
  */
26
19
  export class InvitationsServiceImpl implements InvitationsService {
27
- private readonly _createInvitations = new Map<string, CancellableInvitation>();
28
- private readonly _acceptInvitations = new Map<string, AuthenticatingInvitation>();
29
- private readonly _invitationCreated = new Event<Invitation>();
30
- private readonly _invitationAccepted = new Event<Invitation>();
31
- private readonly _removedCreated = new Event<Invitation>();
32
- private readonly _removedAccepted = new Event<Invitation>();
33
- private readonly _saved = new Event<Invitation>();
34
- private readonly _persistentInvitationsLoadedEvent = new Event();
35
- private _persistentInvitationsLoaded = false;
36
-
37
- constructor(
38
- private readonly _invitationsHandler: InvitationsHandler,
39
- private readonly _getHandler: (invitation: Invitation) => InvitationProtocol,
40
- private readonly _metadataStore: MetadataStore,
41
- ) {}
20
+ constructor(private readonly _invitationsManager: InvitationsManager) {}
42
21
 
43
22
  // TODO(burdon): Guest/host label.
44
23
  getLoggingContext() {
@@ -48,148 +27,31 @@ export class InvitationsServiceImpl implements InvitationsService {
48
27
  }
49
28
 
50
29
  createInvitation(options: Invitation): Stream<Invitation> {
51
- let invitation: CancellableInvitation;
52
-
53
- const savePersistentInvitationCtx = new Context();
54
- const existingInvitation = this._createInvitations.get(options.invitationId);
55
- if (existingInvitation) {
56
- invitation = existingInvitation;
57
- } else {
58
- const handler = this._getHandler(options);
59
- invitation = this._invitationsHandler.createInvitation(handler, options);
60
- this._createInvitations.set(invitation.get().invitationId, invitation);
61
- this._invitationCreated.emit(invitation.get());
62
- }
63
-
30
+ const invitation = this._invitationsManager.createInvitation(options);
64
31
  return new Stream<Invitation>(({ next, close }) => {
65
- if (invitation.get().persistent) {
66
- scheduleTask(savePersistentInvitationCtx, async () => {
67
- try {
68
- await this._metadataStore.addInvitation(invitation.get());
69
- this._saved.emit(invitation.get());
70
- } catch (err: any) {
71
- close(err);
72
- }
73
- });
74
- }
75
- invitation.subscribe(
76
- (invitation) => {
77
- next(invitation);
78
- },
79
- async (err: Error) => {
80
- await savePersistentInvitationCtx.dispose();
81
-
82
- // TODO(nf): also remove from storage?
83
- close(err);
84
- },
85
- async () => {
86
- close();
87
- if (invitation.get().persistent) {
88
- await savePersistentInvitationCtx.dispose();
89
- // TODO(nf): remove on all complete conditions?
90
- await this._metadataStore.removeInvitation(invitation.get().invitationId);
91
- }
92
-
93
- this._createInvitations.delete(invitation.get().invitationId);
94
- if (invitation.get().type !== Invitation.Type.MULTIUSE) {
95
- this._removedCreated.emit(invitation.get());
96
- }
97
- },
98
- );
99
- });
100
- }
101
-
102
- async loadPersistentInvitations() {
103
- const persistentInvitations = this._metadataStore.getInvitations();
104
-
105
- // get saved persistent invitations, filter and remove from storage those that have expired.
106
- const freshInvitations = persistentInvitations.filter(async (invitation) => !invitationExpired(invitation));
107
-
108
- const cInvitations = freshInvitations.map((persistentInvitation) => {
109
- invariant(!this._createInvitations.get(persistentInvitation.invitationId), 'invitation already exists');
110
-
111
- const handler = this._getHandler(persistentInvitation);
112
- const invitation = this._invitationsHandler.createInvitation(handler, persistentInvitation);
113
- this._createInvitations.set(invitation.get().invitationId, invitation);
114
- this._invitationCreated.emit(invitation.get());
115
- return persistentInvitation;
32
+ invitation.subscribe(next, close, close);
116
33
  });
117
- this._persistentInvitationsLoadedEvent.emit();
118
- this._persistentInvitationsLoaded = true;
119
- return { invitations: cInvitations };
120
34
  }
121
35
 
122
- acceptInvitation({ invitation: options, deviceProfile }: AcceptInvitationRequest): Stream<Invitation> {
123
- let invitation: AuthenticatingInvitation;
124
-
125
- // TODO(nf): duplicate check in InvitationHandler
126
- if (deviceProfile) {
127
- invariant(options.kind === Invitation.Kind.DEVICE, 'deviceProfile provided for non-device invitation');
128
- }
129
-
130
- const existingInvitation = this._acceptInvitations.get(options.invitationId);
131
- if (existingInvitation) {
132
- invitation = existingInvitation;
133
- } else {
134
- const handler = this._getHandler(options);
135
- invitation = this._invitationsHandler.acceptInvitation(handler, options, deviceProfile);
136
- this._acceptInvitations.set(invitation.get().invitationId, invitation);
137
- this._invitationAccepted.emit(invitation.get());
138
- }
139
-
36
+ acceptInvitation(request: AcceptInvitationRequest): Stream<Invitation> {
37
+ const invitation = this._invitationsManager.acceptInvitation(request);
140
38
  return new Stream<Invitation>(({ next, close }) => {
141
- invitation.subscribe(
142
- (invitation) => {
143
- next(invitation);
144
- },
145
- (err: Error) => {
146
- close(err);
147
- },
148
- () => {
149
- close();
150
- this._acceptInvitations.delete(invitation.get().invitationId);
151
- if (invitation.get().type !== Invitation.Type.MULTIUSE) {
152
- this._removedAccepted.emit(invitation.get());
153
- }
154
- },
155
- );
39
+ invitation.subscribe(next, close, close);
156
40
  });
157
41
  }
158
42
 
159
- async authenticate({ invitationId, authCode }: AuthenticationRequest): Promise<void> {
160
- log('authenticating...');
161
- invariant(invitationId);
162
- const observable = this._acceptInvitations.get(invitationId);
163
- if (!observable) {
164
- log.warn('invalid invitation', { invitationId });
165
- } else {
166
- await observable.authenticate(authCode);
167
- }
43
+ async authenticate(request: AuthenticationRequest): Promise<void> {
44
+ return this._invitationsManager.authenticate(request);
168
45
  }
169
46
 
170
- async cancelInvitation({ invitationId }: { invitationId: string }): Promise<void> {
171
- log('cancelInvitation...', { invitationId });
172
- invariant(invitationId);
173
- const created = this._createInvitations.get(invitationId);
174
- const accepted = this._acceptInvitations.get(invitationId);
175
- if (created) {
176
- await created.cancel();
177
- this._createInvitations.delete(invitationId);
178
- this._removedCreated.emit(created.get());
179
- if (created.get().persistent) {
180
- await this._metadataStore.removeInvitation(created.get().invitationId);
181
- }
182
- } else if (accepted) {
183
- await accepted.cancel();
184
- this._acceptInvitations.delete(invitationId);
185
- this._removedAccepted.emit(accepted.get());
186
- }
47
+ async cancelInvitation(request: { invitationId: string }): Promise<void> {
48
+ return this._invitationsManager.cancelInvitation(request);
187
49
  }
188
50
 
189
51
  queryInvitations(): Stream<QueryInvitationsResponse> {
190
52
  return new Stream<QueryInvitationsResponse>(({ next, ctx }) => {
191
53
  // Push added invitations to the stream.
192
- this._invitationCreated.on(ctx, (invitation) => {
54
+ this._invitationsManager.invitationCreated.on(ctx, (invitation) => {
193
55
  next({
194
56
  action: QueryInvitationsResponse.Action.ADDED,
195
57
  type: QueryInvitationsResponse.Type.CREATED,
@@ -197,7 +59,7 @@ export class InvitationsServiceImpl implements InvitationsService {
197
59
  });
198
60
  });
199
61
 
200
- this._invitationAccepted.on(ctx, (invitation) => {
62
+ this._invitationsManager.invitationAccepted.on(ctx, (invitation) => {
201
63
  next({
202
64
  action: QueryInvitationsResponse.Action.ADDED,
203
65
  type: QueryInvitationsResponse.Type.ACCEPTED,
@@ -206,7 +68,7 @@ export class InvitationsServiceImpl implements InvitationsService {
206
68
  });
207
69
 
208
70
  // Push removed invitations to the stream.
209
- this._removedCreated.on(ctx, (invitation) => {
71
+ this._invitationsManager.removedCreated.on(ctx, (invitation) => {
210
72
  next({
211
73
  action: QueryInvitationsResponse.Action.REMOVED,
212
74
  type: QueryInvitationsResponse.Type.CREATED,
@@ -214,7 +76,7 @@ export class InvitationsServiceImpl implements InvitationsService {
214
76
  });
215
77
  });
216
78
 
217
- this._removedAccepted.on(ctx, (invitation) => {
79
+ this._invitationsManager.removedAccepted.on(ctx, (invitation) => {
218
80
  next({
219
81
  action: QueryInvitationsResponse.Action.REMOVED,
220
82
  type: QueryInvitationsResponse.Type.ACCEPTED,
@@ -223,7 +85,7 @@ export class InvitationsServiceImpl implements InvitationsService {
223
85
  });
224
86
 
225
87
  // used only for testing
226
- this._saved.on(ctx, (invitation) => {
88
+ this._invitationsManager.saved.on(ctx, (invitation) => {
227
89
  next({
228
90
  action: QueryInvitationsResponse.Action.SAVED,
229
91
  type: QueryInvitationsResponse.Type.CREATED,
@@ -235,33 +97,24 @@ export class InvitationsServiceImpl implements InvitationsService {
235
97
  next({
236
98
  action: QueryInvitationsResponse.Action.ADDED,
237
99
  type: QueryInvitationsResponse.Type.CREATED,
238
- invitations: Array.from(this._createInvitations.values()).map((invitation) => invitation.get()),
100
+ invitations: this._invitationsManager.getCreatedInvitations(),
239
101
  existing: true,
240
102
  });
241
103
 
242
104
  next({
243
105
  action: QueryInvitationsResponse.Action.ADDED,
244
106
  type: QueryInvitationsResponse.Type.ACCEPTED,
245
- invitations: Array.from(this._acceptInvitations.values()).map((invitation) => invitation.get()),
107
+ invitations: this._invitationsManager.getAcceptedInvitations(),
246
108
  existing: true,
247
109
  });
248
110
 
249
- if (this._persistentInvitationsLoaded) {
111
+ this._invitationsManager.onPersistentInvitationsLoaded(ctx, () => {
250
112
  next({
251
113
  action: QueryInvitationsResponse.Action.LOAD_COMPLETE,
252
114
  type: QueryInvitationsResponse.Type.CREATED,
253
115
  // TODO(nf): populate with invitations
254
116
  });
255
- } else {
256
- this._persistentInvitationsLoadedEvent.on(ctx, () => {
257
- next({
258
- action: QueryInvitationsResponse.Action.LOAD_COMPLETE,
259
- type: QueryInvitationsResponse.Type.CREATED,
260
- // TODO(nf): populate with invitations
261
- });
262
- });
263
- }
264
-
117
+ });
265
118
  // TODO(nf): expired invitations?
266
119
  });
267
120
  }
@@ -6,8 +6,8 @@ import { expect } from 'chai';
6
6
 
7
7
  import { asyncTimeout, sleep } from '@dxos/async';
8
8
  import { AutomergeHost, DataServiceImpl } from '@dxos/echo-pipeline';
9
+ import { createTestLevel } from '@dxos/echo-pipeline/testing';
9
10
  import { AutomergeContext } from '@dxos/echo-schema';
10
- import { StorageType, createStorage } from '@dxos/random-access-storage';
11
11
  import { afterTest, describe, test } from '@dxos/test';
12
12
 
13
13
  describe('AutomergeHost', () => {
@@ -19,10 +19,16 @@ describe('AutomergeHost', () => {
19
19
  // creates repo and document | replicates repo | finds document in repo
20
20
  //
21
21
 
22
- const storageDirectory = createStorage({ type: StorageType.RAM }).createDirectory();
22
+ const level = createTestLevel();
23
+ await level.open();
24
+ afterTest(() => level.close());
23
25
 
24
- const host = new AutomergeHost({ directory: storageDirectory });
26
+ const host = new AutomergeHost({
27
+ db: level.sublevel('automerge'),
28
+ });
29
+ await host.open();
25
30
  afterTest(() => host.close());
31
+
26
32
  const dataService = new DataServiceImpl(host);
27
33
  const client = new AutomergeContext(dataService);
28
34
  afterTest(() => client.close());
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { MemorySignalManagerContext } from '@dxos/messaging';
6
6
  import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
7
- import { describe, test } from '@dxos/test';
7
+ import { describe, openAndClose, test } from '@dxos/test';
8
8
 
9
9
  import { createServiceContext } from '../testing';
10
10
  import { performInvitation } from '../testing/invitation-utils';
@@ -27,12 +27,15 @@ describe('services/ServiceContext', () => {
27
27
  test('joined space is synchronized on device invitations', async () => {
28
28
  const networkContext = new MemorySignalManagerContext();
29
29
  const device1 = await createServiceContext({ signalContext: networkContext });
30
+ await openAndClose(device1.automergeHost);
30
31
  await device1.createIdentity();
31
32
 
32
33
  const device2 = await createServiceContext({ signalContext: networkContext });
34
+ await openAndClose(device2.automergeHost);
33
35
  await Promise.all(performInvitation({ host: device1, guest: device2, options: { kind: Invitation.Kind.DEVICE } }));
34
36
 
35
37
  const identity2 = await createServiceContext({ signalContext: networkContext });
38
+ await openAndClose(identity2.automergeHost);
36
39
  await identity2.createIdentity();
37
40
  const space1 = await identity2.dataSpaceManager!.createSpace();
38
41
  await Promise.all(
@@ -10,7 +10,7 @@ import { getCredentialAssertion, type CredentialProcessor } from '@dxos/credenti
10
10
  import { failUndefined } from '@dxos/debug';
11
11
  import { AutomergeHost, MetadataStore, SnapshotStore, SpaceManager, valueEncoding } from '@dxos/echo-pipeline';
12
12
  import { FeedFactory, FeedStore } from '@dxos/feed-store';
13
- import { IndexMetadataStore, IndexStore, Indexer } from '@dxos/indexing';
13
+ import { IndexMetadataStore, IndexStore, Indexer, createStorageCallbacks } from '@dxos/indexing';
14
14
  import { invariant } from '@dxos/invariant';
15
15
  import { Keyring } from '@dxos/keyring';
16
16
  import { PublicKey } from '@dxos/keys';
@@ -39,6 +39,7 @@ import {
39
39
  SpaceInvitationProtocol,
40
40
  type InvitationProtocol,
41
41
  } from '../invitations';
42
+ import { InvitationsManager } from '../invitations/invitations-manager';
42
43
  import { DataSpaceManager, type DataSpaceManagerRuntimeParams, type SigningContext } from '../spaces';
43
44
 
44
45
  export type ServiceContextRuntimeParams = IdentityManagerRuntimeParams & DataSpaceManagerRuntimeParams;
@@ -62,6 +63,7 @@ export class ServiceContext extends Resource {
62
63
  public readonly spaceManager: SpaceManager;
63
64
  public readonly identityManager: IdentityManager;
64
65
  public readonly invitations: InvitationsHandler;
66
+ public readonly invitationsManager: InvitationsManager;
65
67
  public readonly automergeHost: AutomergeHost;
66
68
  public readonly indexMetadata: IndexMetadataStore;
67
69
  public readonly indexer: Indexer;
@@ -124,17 +126,23 @@ export class ServiceContext extends Resource {
124
126
 
125
127
  this.automergeHost = new AutomergeHost({
126
128
  directory: storage.createDirectory('automerge'),
127
- metadata: this.indexMetadata,
129
+ db: level.sublevel('automerge'),
130
+ storageCallbacks: createStorageCallbacks({ host: () => this.automergeHost, metadata: this.indexMetadata }),
128
131
  });
129
132
 
130
133
  this.indexer = new Indexer({
131
- indexStore: new IndexStore({ directory: storage.createDirectory('index-store') }),
134
+ indexStore: new IndexStore({ db: level.sublevel('index-storage') }),
132
135
  metadataStore: this.indexMetadata,
133
136
  loadDocuments: createSelectedDocumentsIterator(this.automergeHost),
134
137
  getAllDocuments: createDocumentsIterator(this.automergeHost),
135
138
  });
136
139
 
137
140
  this.invitations = new InvitationsHandler(this.networkManager);
141
+ this.invitationsManager = new InvitationsManager(
142
+ this.invitations,
143
+ (invitation) => this.getInvitationHandler(invitation),
144
+ this.metadataStore,
145
+ );
138
146
 
139
147
  // TODO(burdon): _initialize called in multiple places.
140
148
  // TODO(burdon): Call _initialize on success.
@@ -158,12 +166,17 @@ export class ServiceContext extends Resource {
158
166
  await this.signalManager.open();
159
167
  await this.networkManager.open();
160
168
 
169
+ await this.automergeHost.open();
161
170
  await this.metadataStore.load();
162
171
  await this.spaceManager.open();
163
172
  await this.identityManager.open(ctx);
164
173
  if (this.identityManager.identity) {
165
174
  await this._initialize(ctx);
166
175
  }
176
+
177
+ const loadedInvitations = await this.invitationsManager.loadPersistentInvitations();
178
+ log('loaded persistent invitations', { count: loadedInvitations.invitations?.length });
179
+
167
180
  log.trace('dxos.sdk.service-context.open', trace.end({ id: this._instanceId }));
168
181
  log('opened');
169
182
  }
@@ -8,8 +8,14 @@ import { Event, synchronized } from '@dxos/async';
8
8
  import { clientServiceBundle, defaultKey, type ClientServices, Properties } from '@dxos/client-protocol';
9
9
  import { type Config } from '@dxos/config';
10
10
  import { Context } from '@dxos/context';
11
- import { DataServiceImpl, type ObjectStructure, encodeReference, type SpaceDoc } from '@dxos/echo-pipeline';
12
- import * as E from '@dxos/echo-schema';
11
+ import {
12
+ DataServiceImpl,
13
+ type ObjectStructure,
14
+ encodeReference,
15
+ type SpaceDoc,
16
+ type LevelDB,
17
+ } from '@dxos/echo-pipeline';
18
+ import { getTypeReference } from '@dxos/echo-schema';
13
19
  import { IndexServiceImpl } from '@dxos/indexing';
14
20
  import { invariant } from '@dxos/invariant';
15
21
  import { PublicKey } from '@dxos/keys';
@@ -50,6 +56,7 @@ export type ClientServicesHostParams = {
50
56
  signalManager?: SignalManager;
51
57
  connectionLog?: boolean;
52
58
  storage?: Storage;
59
+ level?: LevelDB;
53
60
  lockKey?: string;
54
61
  callbacks?: ClientServicesHostCallbacks;
55
62
  runtimeParams?: ServiceContextRuntimeParams;
@@ -101,12 +108,14 @@ export class ClientServicesHost {
101
108
  transportFactory,
102
109
  signalManager,
103
110
  storage,
111
+ level,
104
112
  // TODO(wittjosiah): Turn this on by default.
105
113
  lockKey,
106
114
  callbacks,
107
115
  runtimeParams,
108
116
  }: ClientServicesHostParams = {}) {
109
117
  this._storage = storage;
118
+ this._level = level;
110
119
  this._callbacks = callbacks;
111
120
  this._runtimeParams = runtimeParams;
112
121
 
@@ -239,6 +248,8 @@ export class ClientServicesHost {
239
248
  if (!this._level) {
240
249
  this._level = await createLevel(this._config.get('runtime.client.storage', {})!);
241
250
  }
251
+ await this._level.open();
252
+
242
253
  await this._resourceLock?.acquire();
243
254
 
244
255
  await this._loggingService.open();
@@ -261,11 +272,7 @@ export class ClientServicesHost {
261
272
  (profile) => this._serviceContext.broadcastProfileUpdate(profile),
262
273
  ),
263
274
 
264
- InvitationsService: new InvitationsServiceImpl(
265
- this._serviceContext.invitations,
266
- (invitation) => this._serviceContext.getInvitationHandler(invitation),
267
- this._serviceContext.metadataStore,
268
- ),
275
+ InvitationsService: new InvitationsServiceImpl(this._serviceContext.invitationsManager),
269
276
 
270
277
  DevicesService: new DevicesServiceImpl(this._serviceContext.identityManager),
271
278
 
@@ -299,11 +306,6 @@ export class ClientServicesHost {
299
306
  });
300
307
 
301
308
  await this._serviceContext.open(ctx);
302
- // TODO(nf): move to InvitationManager in ServiceContext?
303
- invariant(this.serviceRegistry.services.InvitationsService);
304
- const loadedInvitations = await this.serviceRegistry.services.InvitationsService.loadPersistentInvitations();
305
-
306
- log('loaded persistent invitations', { count: loadedInvitations.invitations?.length });
307
309
 
308
310
  const devtoolsProxy = this._config?.get('runtime.client.devtoolsProxy');
309
311
  if (devtoolsProxy) {
@@ -372,7 +374,7 @@ export class ClientServicesHost {
372
374
  // TODO(dmaretskyi): Better API for low-level data access.
373
375
  const properties: ObjectStructure = {
374
376
  system: {
375
- type: encodeReference(E.getTypeReference(Properties)!),
377
+ type: encodeReference(getTypeReference(Properties)!),
376
378
  },
377
379
  data: {
378
380
  [defaultKey]: identity.identityKey.toHex(),
@@ -20,7 +20,7 @@ describe('DataSpaceManager', () => {
20
20
 
21
21
  const peer = builder.createPeer();
22
22
  await peer.createIdentity();
23
- await openAndClose(peer.dataSpaceManager);
23
+ await openAndClose(peer.automergeHost, peer.dataSpaceManager);
24
24
 
25
25
  const space = await peer.dataSpaceManager.createSpace();
26
26
 
@@ -42,7 +42,7 @@ describe('DataSpaceManager', () => {
42
42
  const peer2 = builder.createPeer();
43
43
  await peer2.createIdentity();
44
44
 
45
- await openAndClose(peer1.dataSpaceManager, peer2.dataSpaceManager);
45
+ await openAndClose(peer1.automergeHost, peer1.dataSpaceManager, peer2.automergeHost, peer2.dataSpaceManager);
46
46
 
47
47
  const space1 = await peer1.dataSpaceManager.createSpace();
48
48
  await space1.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
@@ -111,7 +111,7 @@ describe('DataSpaceManager', () => {
111
111
  await peer2.createIdentity();
112
112
  await peer2.dataSpaceManager.open();
113
113
 
114
- await openAndClose(peer1.dataSpaceManager, peer2.dataSpaceManager);
114
+ await openAndClose(peer1.automergeHost, peer1.dataSpaceManager, peer2.automergeHost, peer2.dataSpaceManager);
115
115
 
116
116
  const space1 = await peer1.dataSpaceManager.createSpace();
117
117
  await space1.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
@@ -153,7 +153,7 @@ describe('DataSpaceManager', () => {
153
153
 
154
154
  const peer = builder.createPeer();
155
155
  await peer.createIdentity();
156
- await openAndClose(peer.dataSpaceManager);
156
+ await openAndClose(peer.automergeHost, peer.dataSpaceManager);
157
157
 
158
158
  const space = await peer.dataSpaceManager.createSpace();
159
159
  await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
@@ -12,7 +12,7 @@ import { getRootPath, isPersistent } from './util';
12
12
 
13
13
  export const createLevel = async (config: Runtime.Client.Storage) => {
14
14
  const persistent = isPersistent(config);
15
- const storagePath = persistent ? getRootPath(config) : path.join('tmp', 'level', PublicKey.random().toHex());
15
+ const storagePath = persistent ? path.join(getRootPath(config), 'level') : `/tmp/dxos-${PublicKey.random().toHex()}`;
16
16
  const level = new Level<string, string>(storagePath);
17
17
  await level.open();
18
18
  return level;
@@ -6,9 +6,16 @@ import { type Config } from '@dxos/config';
6
6
  import { Context } from '@dxos/context';
7
7
  import { createCredentialSignerWithChain, CredentialGenerator } from '@dxos/credentials';
8
8
  import { failUndefined } from '@dxos/debug';
9
- import { AutomergeHost, MetadataStore, SnapshotStore, SpaceManager, valueEncoding } from '@dxos/echo-pipeline';
9
+ import {
10
+ AutomergeHost,
11
+ MetadataStore,
12
+ type LevelDB,
13
+ SnapshotStore,
14
+ SpaceManager,
15
+ valueEncoding,
16
+ } from '@dxos/echo-pipeline';
17
+ import { createTestLevel } from '@dxos/echo-pipeline/testing';
10
18
  import { FeedFactory, FeedStore } from '@dxos/feed-store';
11
- import { createTestLevel } from '@dxos/indexing/testing';
12
19
  import { Keyring } from '@dxos/keyring';
13
20
  import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
14
21
  import { MemoryTransportFactory, NetworkManager } from '@dxos/network-manager';
@@ -42,7 +49,8 @@ export const createServiceContext = async ({
42
49
  signalManager,
43
50
  transportFactory: MemoryTransportFactory,
44
51
  });
45
- const level = await createTestLevel();
52
+ const level = createTestLevel();
53
+ await level.open();
46
54
 
47
55
  return new ServiceContext(storage, level, networkManager, signalManager);
48
56
  };
@@ -85,6 +93,7 @@ export type TestPeerOpts = {
85
93
 
86
94
  export type TestPeerProps = {
87
95
  storage?: Storage;
96
+ level?: LevelDB;
88
97
  feedStore?: FeedStore<any>;
89
98
  metadataStore?: MetadataStore;
90
99
  keyring?: Keyring;
@@ -117,6 +126,10 @@ export class TestPeer {
117
126
  return (this._props.keyring ??= new Keyring(this.storage.createDirectory('keyring')));
118
127
  }
119
128
 
129
+ get level() {
130
+ return (this._props.level ??= createTestLevel());
131
+ }
132
+
120
133
  get feedStore() {
121
134
  return (this._props.feedStore ??= new FeedStore({
122
135
  factory: new FeedFactory({
@@ -163,7 +176,9 @@ export class TestPeer {
163
176
  }
164
177
 
165
178
  get automergeHost() {
166
- return (this._props.automergeHost ??= new AutomergeHost({ directory: this.storage.createDirectory('automerge') }));
179
+ return (this._props.automergeHost ??= new AutomergeHost({
180
+ db: this.level.sublevel('automerge'),
181
+ }));
167
182
  }
168
183
 
169
184
  get dataSpaceManager() {
@@ -182,6 +197,7 @@ export class TestPeer {
182
197
  }
183
198
 
184
199
  async destroy() {
200
+ await this.level.close();
185
201
  await this.storage.reset();
186
202
  }
187
203
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const DXOS_VERSION = "0.4.10-main.c42bfdb";
1
+ export const DXOS_VERSION = "0.4.10-main.c75170d";