@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.
- package/dist/lib/browser/{chunk-W7UANCHR.mjs → chunk-JP7F2IH3.mjs} +513 -403
- package/dist/lib/browser/chunk-JP7F2IH3.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +5 -3
- package/dist/lib/browser/index.mjs.map +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +9 -4
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-JSVLZGJM.cjs → chunk-34EZSH65.cjs} +520 -411
- package/dist/lib/node/chunk-34EZSH65.cjs.map +7 -0
- package/dist/lib/node/index.cjs +45 -43
- package/dist/lib/node/index.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +14 -9
- package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
- package/dist/types/src/packlets/indexing/util.d.ts +2 -1
- package/dist/types/src/packlets/indexing/util.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/index.d.ts +1 -0
- package/dist/types/src/packlets/invitations/index.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-extension.d.ts +1 -0
- package/dist/types/src/packlets/invitations/invitation-extension.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +4 -2
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts +42 -0
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/invitations-service.d.ts +7 -23
- package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +2 -0
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +3 -1
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +3 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/package.json +34 -34
- package/src/packlets/indexing/util.ts +2 -2
- package/src/packlets/invitations/index.ts +1 -0
- package/src/packlets/invitations/invitation-extension.ts +28 -1
- package/src/packlets/invitations/invitations-handler.ts +74 -34
- package/src/packlets/invitations/invitations-manager.ts +197 -0
- package/src/packlets/invitations/invitations-service.ts +21 -168
- package/src/packlets/services/automerge-host.test.ts +9 -3
- package/src/packlets/services/service-context.test.ts +4 -1
- package/src/packlets/services/service-context.ts +16 -3
- package/src/packlets/services/service-host.ts +15 -13
- package/src/packlets/spaces/data-space-manager.test.ts +4 -4
- package/src/packlets/storage/level.ts +1 -1
- package/src/packlets/testing/test-builder.ts +20 -4
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-W7UANCHR.mjs.map +0 -7
- 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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
123
|
-
|
|
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(
|
|
160
|
-
|
|
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(
|
|
171
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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:
|
|
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:
|
|
107
|
+
invitations: this._invitationsManager.getAcceptedInvitations(),
|
|
246
108
|
existing: true,
|
|
247
109
|
});
|
|
248
110
|
|
|
249
|
-
|
|
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
|
-
}
|
|
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
|
|
22
|
+
const level = createTestLevel();
|
|
23
|
+
await level.open();
|
|
24
|
+
afterTest(() => level.close());
|
|
23
25
|
|
|
24
|
-
const host = new AutomergeHost({
|
|
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
|
-
|
|
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({
|
|
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 {
|
|
12
|
-
|
|
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(
|
|
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)
|
|
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 {
|
|
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 =
|
|
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({
|
|
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.
|
|
1
|
+
export const DXOS_VERSION = "0.4.10-main.c75170d";
|