@dxos/client-services 0.1.0
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/LICENSE +21 -0
- package/README.md +20 -0
- package/dist/src/errors.d.ts +8 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +21 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +26 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/packlets/devtools/devtools-host-events.d.ts +5 -0
- package/dist/src/packlets/devtools/devtools-host-events.d.ts.map +1 -0
- package/dist/src/packlets/devtools/devtools-host-events.js +14 -0
- package/dist/src/packlets/devtools/devtools-host-events.js.map +1 -0
- package/dist/src/packlets/devtools/feeds.d.ts +11 -0
- package/dist/src/packlets/devtools/feeds.d.ts.map +1 -0
- package/dist/src/packlets/devtools/feeds.js +72 -0
- package/dist/src/packlets/devtools/feeds.js.map +1 -0
- package/dist/src/packlets/devtools/index.d.ts +5 -0
- package/dist/src/packlets/devtools/index.d.ts.map +1 -0
- package/dist/src/packlets/devtools/index.js +24 -0
- package/dist/src/packlets/devtools/index.js.map +1 -0
- package/dist/src/packlets/devtools/network.d.ts +19 -0
- package/dist/src/packlets/devtools/network.d.ts.map +1 -0
- package/dist/src/packlets/devtools/network.js +79 -0
- package/dist/src/packlets/devtools/network.js.map +1 -0
- package/dist/src/packlets/devtools/spaces.d.ts +5 -0
- package/dist/src/packlets/devtools/spaces.d.ts.map +1 -0
- package/dist/src/packlets/devtools/spaces.js +38 -0
- package/dist/src/packlets/devtools/spaces.js.map +1 -0
- package/dist/src/packlets/identity/authenticator.d.ts +7 -0
- package/dist/src/packlets/identity/authenticator.d.ts.map +1 -0
- package/dist/src/packlets/identity/authenticator.js +40 -0
- package/dist/src/packlets/identity/authenticator.js.map +1 -0
- package/dist/src/packlets/identity/authenticator.test.d.ts +2 -0
- package/dist/src/packlets/identity/authenticator.test.d.ts.map +1 -0
- package/dist/src/packlets/identity/authenticator.test.js +32 -0
- package/dist/src/packlets/identity/authenticator.test.js.map +1 -0
- package/dist/src/packlets/identity/identity-manager.d.ts +35 -0
- package/dist/src/packlets/identity/identity-manager.d.ts.map +1 -0
- package/dist/src/packlets/identity/identity-manager.js +151 -0
- package/dist/src/packlets/identity/identity-manager.js.map +1 -0
- package/dist/src/packlets/identity/identity-manager.test.d.ts +2 -0
- package/dist/src/packlets/identity/identity-manager.test.d.ts.map +1 -0
- package/dist/src/packlets/identity/identity-manager.test.js +92 -0
- package/dist/src/packlets/identity/identity-manager.test.js.map +1 -0
- package/dist/src/packlets/identity/identity.d.ts +43 -0
- package/dist/src/packlets/identity/identity.d.ts.map +1 -0
- package/dist/src/packlets/identity/identity.js +74 -0
- package/dist/src/packlets/identity/identity.js.map +1 -0
- package/dist/src/packlets/identity/identity.test.d.ts +2 -0
- package/dist/src/packlets/identity/identity.test.d.ts.map +1 -0
- package/dist/src/packlets/identity/identity.test.js +258 -0
- package/dist/src/packlets/identity/identity.test.js.map +1 -0
- package/dist/src/packlets/identity/index.d.ts +4 -0
- package/dist/src/packlets/identity/index.d.ts.map +1 -0
- package/dist/src/packlets/identity/index.js +23 -0
- package/dist/src/packlets/identity/index.js.map +1 -0
- package/dist/src/packlets/invitations/common.d.ts +35 -0
- package/dist/src/packlets/invitations/common.d.ts.map +1 -0
- package/dist/src/packlets/invitations/common.js +12 -0
- package/dist/src/packlets/invitations/common.js.map +1 -0
- package/dist/src/packlets/invitations/halo-invitations.d.ts +23 -0
- package/dist/src/packlets/invitations/halo-invitations.d.ts.map +1 -0
- package/dist/src/packlets/invitations/halo-invitations.js +150 -0
- package/dist/src/packlets/invitations/halo-invitations.js.map +1 -0
- package/dist/src/packlets/invitations/index.d.ts +5 -0
- package/dist/src/packlets/invitations/index.d.ts.map +1 -0
- package/dist/src/packlets/invitations/index.js +24 -0
- package/dist/src/packlets/invitations/index.js.map +1 -0
- package/dist/src/packlets/invitations/invitation-wrapper.d.ts +36 -0
- package/dist/src/packlets/invitations/invitation-wrapper.d.ts.map +1 -0
- package/dist/src/packlets/invitations/invitation-wrapper.js +104 -0
- package/dist/src/packlets/invitations/invitation-wrapper.js.map +1 -0
- package/dist/src/packlets/invitations/observable.test.d.ts +2 -0
- package/dist/src/packlets/invitations/observable.test.d.ts.map +1 -0
- package/dist/src/packlets/invitations/observable.test.js +43 -0
- package/dist/src/packlets/invitations/observable.test.js.map +1 -0
- package/dist/src/packlets/invitations/space-invitations.d.ts +25 -0
- package/dist/src/packlets/invitations/space-invitations.d.ts.map +1 -0
- package/dist/src/packlets/invitations/space-invitations.js +136 -0
- package/dist/src/packlets/invitations/space-invitations.js.map +1 -0
- package/dist/src/packlets/services/impl/devtools.d.ts +34 -0
- package/dist/src/packlets/services/impl/devtools.d.ts.map +1 -0
- package/dist/src/packlets/services/impl/devtools.js +77 -0
- package/dist/src/packlets/services/impl/devtools.js.map +1 -0
- package/dist/src/packlets/services/impl/halo.d.ts +20 -0
- package/dist/src/packlets/services/impl/halo.d.ts.map +1 -0
- package/dist/src/packlets/services/impl/halo.js +93 -0
- package/dist/src/packlets/services/impl/halo.js.map +1 -0
- package/dist/src/packlets/services/impl/index.d.ts +6 -0
- package/dist/src/packlets/services/impl/index.d.ts.map +1 -0
- package/dist/src/packlets/services/impl/index.js +26 -0
- package/dist/src/packlets/services/impl/index.js.map +1 -0
- package/dist/src/packlets/services/impl/party.d.ts +25 -0
- package/dist/src/packlets/services/impl/party.d.ts.map +1 -0
- package/dist/src/packlets/services/impl/party.js +265 -0
- package/dist/src/packlets/services/impl/party.js.map +1 -0
- package/dist/src/packlets/services/impl/profile.d.ts +19 -0
- package/dist/src/packlets/services/impl/profile.d.ts.map +1 -0
- package/dist/src/packlets/services/impl/profile.js +120 -0
- package/dist/src/packlets/services/impl/profile.js.map +1 -0
- package/dist/src/packlets/services/impl/system.d.ts +9 -0
- package/dist/src/packlets/services/impl/system.d.ts.map +1 -0
- package/dist/src/packlets/services/impl/system.js +20 -0
- package/dist/src/packlets/services/impl/system.js.map +1 -0
- package/dist/src/packlets/services/impl/tracing.d.ts +12 -0
- package/dist/src/packlets/services/impl/tracing.d.ts.map +1 -0
- package/dist/src/packlets/services/impl/tracing.js +22 -0
- package/dist/src/packlets/services/impl/tracing.js.map +1 -0
- package/dist/src/packlets/services/index.d.ts +6 -0
- package/dist/src/packlets/services/index.d.ts.map +1 -0
- package/dist/src/packlets/services/index.js +25 -0
- package/dist/src/packlets/services/index.js.map +1 -0
- package/dist/src/packlets/services/service-context.d.ts +36 -0
- package/dist/src/packlets/services/service-context.d.ts.map +1 -0
- package/dist/src/packlets/services/service-context.js +112 -0
- package/dist/src/packlets/services/service-context.js.map +1 -0
- package/dist/src/packlets/services/service-factory.d.ts +15 -0
- package/dist/src/packlets/services/service-factory.d.ts.map +1 -0
- package/dist/src/packlets/services/service-factory.js +25 -0
- package/dist/src/packlets/services/service-factory.js.map +1 -0
- package/dist/src/packlets/services/service-host.d.ts +27 -0
- package/dist/src/packlets/services/service-host.d.ts.map +1 -0
- package/dist/src/packlets/services/service-host.js +78 -0
- package/dist/src/packlets/services/service-host.js.map +1 -0
- package/dist/src/packlets/services/service-host.test.d.ts +2 -0
- package/dist/src/packlets/services/service-host.test.d.ts.map +1 -0
- package/dist/src/packlets/services/service-host.test.js +78 -0
- package/dist/src/packlets/services/service-host.test.js.map +1 -0
- package/dist/src/packlets/services/services.d.ts +19 -0
- package/dist/src/packlets/services/services.d.ts.map +1 -0
- package/dist/src/packlets/services/services.js +19 -0
- package/dist/src/packlets/services/services.js.map +1 -0
- package/dist/src/packlets/services/signer.d.ts +9 -0
- package/dist/src/packlets/services/signer.d.ts.map +1 -0
- package/dist/src/packlets/services/signer.js +6 -0
- package/dist/src/packlets/services/signer.js.map +1 -0
- package/dist/src/packlets/services/testing/halo.test.d.ts +2 -0
- package/dist/src/packlets/services/testing/halo.test.d.ts.map +1 -0
- package/dist/src/packlets/services/testing/halo.test.js +34 -0
- package/dist/src/packlets/services/testing/halo.test.js.map +1 -0
- package/dist/src/packlets/services/testing/spaces.test.d.ts +2 -0
- package/dist/src/packlets/services/testing/spaces.test.d.ts.map +1 -0
- package/dist/src/packlets/services/testing/spaces.test.js +68 -0
- package/dist/src/packlets/services/testing/spaces.test.js.map +1 -0
- package/dist/src/packlets/services/testing/testing.d.ts +8 -0
- package/dist/src/packlets/services/testing/testing.d.ts.map +1 -0
- package/dist/src/packlets/services/testing/testing.js +23 -0
- package/dist/src/packlets/services/testing/testing.js.map +1 -0
- package/dist/src/packlets/storage/index.d.ts +2 -0
- package/dist/src/packlets/storage/index.d.ts.map +1 -0
- package/dist/src/packlets/storage/index.js +21 -0
- package/dist/src/packlets/storage/index.js.map +1 -0
- package/dist/src/packlets/storage/storage.d.ts +7 -0
- package/dist/src/packlets/storage/storage.d.ts.map +1 -0
- package/dist/src/packlets/storage/storage.js +89 -0
- package/dist/src/packlets/storage/storage.js.map +1 -0
- package/package.json +61 -0
- package/src/errors.ts +19 -0
- package/src/index.ts +11 -0
- package/src/packlets/devtools/devtools-host-events.ts +8 -0
- package/src/packlets/devtools/feeds.ts +91 -0
- package/src/packlets/devtools/index.ts +8 -0
- package/src/packlets/devtools/network.ts +89 -0
- package/src/packlets/devtools/spaces.ts +50 -0
- package/src/packlets/identity/authenticator.test.ts +32 -0
- package/src/packlets/identity/authenticator.ts +49 -0
- package/src/packlets/identity/identity-manager.test.ts +121 -0
- package/src/packlets/identity/identity-manager.ts +207 -0
- package/src/packlets/identity/identity.test.ts +295 -0
- package/src/packlets/identity/identity.ts +109 -0
- package/src/packlets/identity/index.ts +7 -0
- package/src/packlets/invitations/common.ts +48 -0
- package/src/packlets/invitations/halo-invitations.ts +162 -0
- package/src/packlets/invitations/index.ts +8 -0
- package/src/packlets/invitations/invitation-wrapper.ts +140 -0
- package/src/packlets/invitations/observable.test.ts +67 -0
- package/src/packlets/invitations/space-invitations.ts +159 -0
- package/src/packlets/services/impl/devtools.ts +141 -0
- package/src/packlets/services/impl/halo.ts +107 -0
- package/src/packlets/services/impl/index.ts +11 -0
- package/src/packlets/services/impl/party.ts +305 -0
- package/src/packlets/services/impl/profile.ts +141 -0
- package/src/packlets/services/impl/system.ts +19 -0
- package/src/packlets/services/impl/tracing.ts +21 -0
- package/src/packlets/services/index.ts +9 -0
- package/src/packlets/services/service-context.ts +151 -0
- package/src/packlets/services/service-factory.ts +35 -0
- package/src/packlets/services/service-host.test.ts +88 -0
- package/src/packlets/services/service-host.ts +101 -0
- package/src/packlets/services/services.ts +39 -0
- package/src/packlets/services/signer.ts +13 -0
- package/src/packlets/services/testing/halo.test.ts +40 -0
- package/src/packlets/services/testing/spaces.test.ts +87 -0
- package/src/packlets/services/testing/testing.ts +28 -0
- package/src/packlets/storage/index.ts +5 -0
- package/src/packlets/storage/storage.ts +97 -0
- package/src/typings.d.ts +5 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import assert from 'node:assert';
|
|
6
|
+
import { v4 } from 'uuid';
|
|
7
|
+
|
|
8
|
+
import { latch } from '@dxos/async';
|
|
9
|
+
import { Stream } from '@dxos/codec-protobuf';
|
|
10
|
+
import { generatePasscode } from '@dxos/credentials';
|
|
11
|
+
import { todo } from '@dxos/debug';
|
|
12
|
+
import {
|
|
13
|
+
AuthenticateInvitationRequest,
|
|
14
|
+
CreateProfileRequest,
|
|
15
|
+
InvitationRequest,
|
|
16
|
+
InvitationState,
|
|
17
|
+
Profile,
|
|
18
|
+
ProfileService as ProfileServiceRpc,
|
|
19
|
+
RecoverProfileRequest,
|
|
20
|
+
RedeemedInvitation,
|
|
21
|
+
SubscribeProfileResponse
|
|
22
|
+
} from '@dxos/protocols/proto/dxos/client';
|
|
23
|
+
import { InvitationDescriptor } from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
24
|
+
|
|
25
|
+
import { InviteeInvitation, InviteeInvitations } from '../../invitations';
|
|
26
|
+
import { ServiceContext } from '../service-context';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Profile service implementation.
|
|
30
|
+
*/
|
|
31
|
+
export class ProfileService implements ProfileServiceRpc {
|
|
32
|
+
private inviteeInvitations: InviteeInvitations = new Map();
|
|
33
|
+
|
|
34
|
+
constructor(private readonly context: ServiceContext) {}
|
|
35
|
+
|
|
36
|
+
subscribeProfile(): Stream<SubscribeProfileResponse> {
|
|
37
|
+
return new Stream(({ next }) => {
|
|
38
|
+
const emitNext = () =>
|
|
39
|
+
next({
|
|
40
|
+
profile: this.context.identityManager.identity
|
|
41
|
+
? {
|
|
42
|
+
publicKey: this.context.identityManager.identity.identityKey
|
|
43
|
+
}
|
|
44
|
+
: undefined
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
emitNext();
|
|
48
|
+
return this.context.identityManager.stateUpdate.on(emitNext);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async createProfile(request: CreateProfileRequest): Promise<Profile> {
|
|
53
|
+
await this.context.createIdentity();
|
|
54
|
+
return { publicKey: this.context.identityManager.identity!.identityKey };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async recoverProfile(request: RecoverProfileRequest): Promise<Profile> {
|
|
58
|
+
return todo();
|
|
59
|
+
if (!request.seedPhrase) {
|
|
60
|
+
throw new Error('Recovery SeedPhrase not provided.');
|
|
61
|
+
}
|
|
62
|
+
// await this.echo.open();
|
|
63
|
+
// await this.echo.halo.recover(request.seed_phrase);
|
|
64
|
+
// const profile = this.echo.halo.getProfile();
|
|
65
|
+
// assert(profile, 'Recovering profile failed.');
|
|
66
|
+
// return profile;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
createInvitation(): Stream<InvitationRequest> {
|
|
70
|
+
return new Stream(({ next, close }) => {
|
|
71
|
+
setTimeout(async () => {
|
|
72
|
+
const secret = Buffer.from(generatePasscode());
|
|
73
|
+
// TODO(burdon): Not used.
|
|
74
|
+
// const secretProvider = async () => {
|
|
75
|
+
// next({ descriptor: invitation.toProto(), state: InvitationState.CONNECTED });
|
|
76
|
+
// return Buffer.from(secret);
|
|
77
|
+
// };
|
|
78
|
+
|
|
79
|
+
const invitation = await this.context.haloInvitations.createInvitation({
|
|
80
|
+
onFinish: () => {
|
|
81
|
+
next({ state: InvitationState.SUCCESS });
|
|
82
|
+
close();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
invitation.secret = secret;
|
|
86
|
+
|
|
87
|
+
next({
|
|
88
|
+
state: InvitationState.WAITING_FOR_CONNECTION,
|
|
89
|
+
descriptor: invitation
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
acceptInvitation(invitation: InvitationDescriptor): Stream<RedeemedInvitation> {
|
|
96
|
+
return new Stream(({ next, close }) => {
|
|
97
|
+
const id = v4();
|
|
98
|
+
const [, secretTrigger] = latch();
|
|
99
|
+
const inviteeInvitation: InviteeInvitation = { secretTrigger };
|
|
100
|
+
|
|
101
|
+
// Secret will be provided separately (in AuthenticateInvitation).
|
|
102
|
+
// Process will continue when `secretLatch` resolves, triggered by `secretTrigger`.
|
|
103
|
+
// const secretProvider: SecretProvider = async () => {
|
|
104
|
+
// await secretLatch;
|
|
105
|
+
// const secret = inviteeInvitation.secret;
|
|
106
|
+
// if (!secret) {
|
|
107
|
+
// throw new Error('Secret not provided.');
|
|
108
|
+
// }
|
|
109
|
+
// return Buffer.from(secret);
|
|
110
|
+
// };
|
|
111
|
+
|
|
112
|
+
// Joining process is kicked off, and will await authentication with a secret.
|
|
113
|
+
const haloPartyPromise = this.context.haloInvitations.acceptInvitation(invitation);
|
|
114
|
+
this.inviteeInvitations.set(id, inviteeInvitation);
|
|
115
|
+
next({ id, state: InvitationState.CONNECTED });
|
|
116
|
+
|
|
117
|
+
haloPartyPromise
|
|
118
|
+
.then((identity) => {
|
|
119
|
+
next({
|
|
120
|
+
id,
|
|
121
|
+
state: InvitationState.SUCCESS,
|
|
122
|
+
partyKey: identity.identityKey
|
|
123
|
+
});
|
|
124
|
+
})
|
|
125
|
+
.catch((err) => {
|
|
126
|
+
next({ id, state: InvitationState.ERROR, error: String(err) });
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async authenticateInvitation(request: AuthenticateInvitationRequest) {
|
|
132
|
+
assert(request.processId, 'Process ID is missing.');
|
|
133
|
+
const invitation = this.inviteeInvitations.get(request.processId);
|
|
134
|
+
assert(invitation, 'Invitation not found.');
|
|
135
|
+
assert(request.secret, 'Secret not provided.');
|
|
136
|
+
|
|
137
|
+
// Supply the secret, and move the internal invitation process by triggering the secretTrigger.
|
|
138
|
+
invitation.secret = request.secret;
|
|
139
|
+
invitation.secretTrigger?.();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Config } from '@dxos/config';
|
|
6
|
+
import { todo } from '@dxos/debug';
|
|
7
|
+
import { SystemService as SystemServiceRpc } from '@dxos/protocols/proto/dxos/client';
|
|
8
|
+
|
|
9
|
+
export class SystemService implements SystemServiceRpc {
|
|
10
|
+
constructor(private readonly _config: Config) {}
|
|
11
|
+
|
|
12
|
+
async getConfig(request: void) {
|
|
13
|
+
return this._config.values;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async reset(request: any) {
|
|
17
|
+
todo();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Config } from '@dxos/config';
|
|
6
|
+
import { TracingService as TracingServiceRpc } from '@dxos/protocols/proto/dxos/devtools/host';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
export class TracingService implements TracingServiceRpc {
|
|
12
|
+
constructor(private readonly _config: Config) {}
|
|
13
|
+
|
|
14
|
+
async setTracingOptions() {
|
|
15
|
+
throw new Error('Tracing not available.');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
subscribeToRpcTrace(): any {
|
|
19
|
+
throw new Error('Tracing not available.');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import assert from 'node:assert';
|
|
6
|
+
|
|
7
|
+
import { Trigger } from '@dxos/async';
|
|
8
|
+
import { failUndefined, raise } from '@dxos/debug';
|
|
9
|
+
import {
|
|
10
|
+
MOCK_AUTH_PROVIDER,
|
|
11
|
+
MOCK_AUTH_VERIFIER,
|
|
12
|
+
valueEncoding,
|
|
13
|
+
DataService,
|
|
14
|
+
MetadataStore,
|
|
15
|
+
SpaceManager,
|
|
16
|
+
SigningContext
|
|
17
|
+
} from '@dxos/echo-db';
|
|
18
|
+
import { FeedFactory, FeedStore } from '@dxos/feed-store';
|
|
19
|
+
import { Keyring } from '@dxos/keyring';
|
|
20
|
+
import { PublicKey } from '@dxos/keys';
|
|
21
|
+
import { log } from '@dxos/log';
|
|
22
|
+
import { ModelFactory } from '@dxos/model-factory';
|
|
23
|
+
import { NetworkManager } from '@dxos/network-manager';
|
|
24
|
+
import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
25
|
+
import { Storage } from '@dxos/random-access-storage';
|
|
26
|
+
|
|
27
|
+
import { IdentityManager } from '../identity';
|
|
28
|
+
import { HaloInvitations, InvitationWrapper, SpaceInvitations } from '../invitations';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Shared backend for all client services.
|
|
32
|
+
*/
|
|
33
|
+
// TODO(burdon): Rename/break-up into smaller components. And/or make members private.
|
|
34
|
+
export class ServiceContext {
|
|
35
|
+
public readonly initialized = new Trigger();
|
|
36
|
+
|
|
37
|
+
// TODO(burdon): Factor out with other services.
|
|
38
|
+
public readonly dataService = new DataService();
|
|
39
|
+
|
|
40
|
+
public readonly metadataStore: MetadataStore;
|
|
41
|
+
public readonly feedStore: FeedStore<FeedMessage>;
|
|
42
|
+
public readonly keyring: Keyring;
|
|
43
|
+
public readonly identityManager: IdentityManager;
|
|
44
|
+
public readonly haloInvitations: HaloInvitations;
|
|
45
|
+
|
|
46
|
+
// Initialized after identity is initialized.
|
|
47
|
+
public spaceManager?: SpaceManager;
|
|
48
|
+
public spaceInvitations?: SpaceInvitations;
|
|
49
|
+
|
|
50
|
+
// prettier-ignore
|
|
51
|
+
constructor(
|
|
52
|
+
public readonly storage: Storage,
|
|
53
|
+
public readonly networkManager: NetworkManager,
|
|
54
|
+
public readonly modelFactory: ModelFactory
|
|
55
|
+
) {
|
|
56
|
+
// TODO(burdon): Move strings to constants.
|
|
57
|
+
this.metadataStore = new MetadataStore(storage.createDirectory('metadata'));
|
|
58
|
+
this.keyring = new Keyring(storage.createDirectory('keyring'));
|
|
59
|
+
this.feedStore = new FeedStore<FeedMessage>({
|
|
60
|
+
factory: new FeedFactory<FeedMessage>({
|
|
61
|
+
root: storage.createDirectory('feeds'),
|
|
62
|
+
signer: this.keyring,
|
|
63
|
+
hypercore: {
|
|
64
|
+
valueEncoding
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
this.identityManager = new IdentityManager(
|
|
70
|
+
this.metadataStore,
|
|
71
|
+
this.feedStore,
|
|
72
|
+
this.keyring,
|
|
73
|
+
networkManager,
|
|
74
|
+
modelFactory
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// TODO(burdon): _initialize called in multiple places.
|
|
78
|
+
this.haloInvitations = new HaloInvitations(this.identityManager, this.networkManager, async () => {
|
|
79
|
+
await this._initialize();
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async open() {
|
|
84
|
+
log('opening...');
|
|
85
|
+
await this.identityManager.open();
|
|
86
|
+
if (this.identityManager.identity) {
|
|
87
|
+
await this._initialize();
|
|
88
|
+
}
|
|
89
|
+
log('opened');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async close() {
|
|
93
|
+
log('closing...');
|
|
94
|
+
await this.identityManager.close();
|
|
95
|
+
await this.spaceManager?.close();
|
|
96
|
+
await this.feedStore.close();
|
|
97
|
+
await this.networkManager.destroy(); // TODO(burdon): Close.
|
|
98
|
+
log('closed');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async createIdentity() {
|
|
102
|
+
const identity = await this.identityManager.createIdentity();
|
|
103
|
+
this.dataService.trackParty(identity.haloSpaceKey, identity.haloDatabase.createDataServiceHost());
|
|
104
|
+
await this._initialize();
|
|
105
|
+
return identity;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async createInvitation(spaceKey: PublicKey, onFinish?: () => void): Promise<InvitationWrapper> {
|
|
109
|
+
assert(this.spaceManager);
|
|
110
|
+
assert(this.spaceInvitations);
|
|
111
|
+
|
|
112
|
+
const space = this.spaceManager.spaces.get(spaceKey) ?? raise(new Error('Space not found.'));
|
|
113
|
+
const invitation = await this.spaceInvitations.createInvitation(space, { onFinish });
|
|
114
|
+
return InvitationWrapper.fromProto(invitation);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async acceptInvitation(invitationDescriptor: InvitationWrapper) {
|
|
118
|
+
assert(this.spaceInvitations);
|
|
119
|
+
|
|
120
|
+
return this.spaceInvitations.acceptInvitation(invitationDescriptor.toProto());
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private async _initialize() {
|
|
124
|
+
const identity = this.identityManager.identity ?? failUndefined();
|
|
125
|
+
const signingContext: SigningContext = {
|
|
126
|
+
credentialProvider: MOCK_AUTH_PROVIDER,
|
|
127
|
+
credentialAuthenticator: MOCK_AUTH_VERIFIER,
|
|
128
|
+
credentialSigner: identity.getIdentityCredentialSigner(),
|
|
129
|
+
identityKey: identity.identityKey,
|
|
130
|
+
deviceKey: identity.deviceKey
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Create in constructor (avoid all of these private variables).
|
|
134
|
+
const spaceManager = new SpaceManager({
|
|
135
|
+
metadataStore: this.metadataStore,
|
|
136
|
+
feedStore: this.feedStore,
|
|
137
|
+
networkManager: this.networkManager,
|
|
138
|
+
keyring: this.keyring,
|
|
139
|
+
dataService: this.dataService,
|
|
140
|
+
modelFactory: this.modelFactory,
|
|
141
|
+
signingContext
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
await spaceManager.open();
|
|
145
|
+
|
|
146
|
+
this.spaceManager = spaceManager;
|
|
147
|
+
this.spaceInvitations = new SpaceInvitations(this.spaceManager, this.networkManager, signingContext);
|
|
148
|
+
|
|
149
|
+
this.initialized.wake();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Config } from '@dxos/config';
|
|
6
|
+
import { NetworkManager } from '@dxos/network-manager';
|
|
7
|
+
|
|
8
|
+
import { DevtoolsHostEvents } from '../devtools';
|
|
9
|
+
import { HaloService, PartyService, ProfileService, SystemService, TracingService } from './impl';
|
|
10
|
+
import { DevtoolsService } from './impl/devtools';
|
|
11
|
+
import { ServiceContext } from './service-context';
|
|
12
|
+
import { ClientServices } from './services';
|
|
13
|
+
import { HaloSigner } from './signer';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Service factory.
|
|
17
|
+
*/
|
|
18
|
+
export const createServices = ({
|
|
19
|
+
config,
|
|
20
|
+
context, // TODO(burdon): Too big to pass into services.?
|
|
21
|
+
signer // TODO(burdon): Remove (legacy?)
|
|
22
|
+
}: {
|
|
23
|
+
config: Config;
|
|
24
|
+
context: ServiceContext;
|
|
25
|
+
networkManager: NetworkManager;
|
|
26
|
+
signer?: HaloSigner;
|
|
27
|
+
}): ClientServices => ({
|
|
28
|
+
DataService: context.dataService,
|
|
29
|
+
HaloService: new HaloService(null, signer), // TODO(burdon): Remove.
|
|
30
|
+
PartyService: new PartyService(context),
|
|
31
|
+
ProfileService: new ProfileService(context),
|
|
32
|
+
SystemService: new SystemService(config),
|
|
33
|
+
TracingService: new TracingService(config),
|
|
34
|
+
DevtoolsHost: new DevtoolsService({ events: new DevtoolsHostEvents(), config, context })
|
|
35
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
|
|
7
|
+
import { latch, Trigger } from '@dxos/async';
|
|
8
|
+
import { Config, ConfigProto } from '@dxos/config';
|
|
9
|
+
import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
|
|
10
|
+
import { MemoryTransportFactory, NetworkManager } from '@dxos/network-manager';
|
|
11
|
+
import { InvitationState } from '@dxos/protocols/proto/dxos/client';
|
|
12
|
+
import { InvitationDescriptor } from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
13
|
+
import { afterTest } from '@dxos/testutils';
|
|
14
|
+
|
|
15
|
+
import { ClientServiceHost } from './service-host';
|
|
16
|
+
|
|
17
|
+
const defaultTestingConfig: ConfigProto = {
|
|
18
|
+
version: 1
|
|
19
|
+
// runtime: {
|
|
20
|
+
// services: {
|
|
21
|
+
// signal: {
|
|
22
|
+
// server: 'ws://localhost:4000/.well-known/dx/signal'
|
|
23
|
+
// }
|
|
24
|
+
// }
|
|
25
|
+
// }
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
describe('services/service-host', function () {
|
|
29
|
+
// TODO(burdon): Factor out.
|
|
30
|
+
const createPeer = (signalManagerContext: MemorySignalManagerContext) => {
|
|
31
|
+
const networkManager = new NetworkManager({
|
|
32
|
+
signalManager: new MemorySignalManager(signalManagerContext),
|
|
33
|
+
transportFactory: MemoryTransportFactory
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return new ClientServiceHost({
|
|
37
|
+
config: new Config(defaultTestingConfig),
|
|
38
|
+
networkManager
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
it('process device invitation', async function () {
|
|
43
|
+
const signalManagerContext = new MemorySignalManagerContext();
|
|
44
|
+
|
|
45
|
+
const peer1 = createPeer(signalManagerContext);
|
|
46
|
+
const peer2 = createPeer(signalManagerContext);
|
|
47
|
+
await peer1.open();
|
|
48
|
+
await peer2.open();
|
|
49
|
+
afterTest(() => peer1.close());
|
|
50
|
+
afterTest(() => peer2.close());
|
|
51
|
+
|
|
52
|
+
const invitationTrigger = new Trigger<InvitationDescriptor>();
|
|
53
|
+
{
|
|
54
|
+
await peer1.services.ProfileService.createProfile({});
|
|
55
|
+
const stream = peer1.services.ProfileService.createInvitation();
|
|
56
|
+
stream.subscribe((msg) => {
|
|
57
|
+
switch (msg.state) {
|
|
58
|
+
case InvitationState.WAITING_FOR_CONNECTION: {
|
|
59
|
+
invitationTrigger.wake(msg.descriptor!);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const [done, setAck] = latch({ count: 2 });
|
|
67
|
+
{
|
|
68
|
+
const invitation = await invitationTrigger.wait();
|
|
69
|
+
const stream = peer2.services.ProfileService.acceptInvitation(invitation);
|
|
70
|
+
stream.subscribe((msg) => {
|
|
71
|
+
switch (msg.state) {
|
|
72
|
+
case InvitationState.CONNECTED: {
|
|
73
|
+
setAck();
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
case InvitationState.SUCCESS: {
|
|
78
|
+
expect(msg.partyKey).not.to.be.undefined;
|
|
79
|
+
setAck();
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
await done();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2021 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import assert from 'assert';
|
|
6
|
+
|
|
7
|
+
import { Config } from '@dxos/config';
|
|
8
|
+
import { log } from '@dxos/log';
|
|
9
|
+
import { MemorySignalManager, MemorySignalManagerContext, WebsocketSignalManager } from '@dxos/messaging';
|
|
10
|
+
import { ModelFactory } from '@dxos/model-factory';
|
|
11
|
+
import { createWebRTCTransportFactory, MemoryTransportFactory, NetworkManager } from '@dxos/network-manager';
|
|
12
|
+
import { ObjectModel } from '@dxos/object-model';
|
|
13
|
+
|
|
14
|
+
import { createStorageObjects } from '../storage';
|
|
15
|
+
import { ServiceContext } from './service-context';
|
|
16
|
+
import { createServices } from './service-factory';
|
|
17
|
+
import { ClientServiceProvider, ClientServices } from './services';
|
|
18
|
+
import { HaloSigner } from './signer';
|
|
19
|
+
// import { DevtoolsHostEvents } from '../devtools';
|
|
20
|
+
|
|
21
|
+
// TODO(burdon): Remove (no memory defaults).
|
|
22
|
+
const memorySignalManagerContext = new MemorySignalManagerContext();
|
|
23
|
+
|
|
24
|
+
// TODO(burdon): Factor out.
|
|
25
|
+
export const createNetworkManager = (config: Config): NetworkManager => {
|
|
26
|
+
const signalServer = config.get('runtime.services.signal.server');
|
|
27
|
+
// TODO(burdon): Remove.
|
|
28
|
+
if (!signalServer) {
|
|
29
|
+
log.warn('DEPRECATED: falling back to MemorySignalManager');
|
|
30
|
+
return new NetworkManager({
|
|
31
|
+
log: true,
|
|
32
|
+
signalManager: new MemorySignalManager(memorySignalManagerContext),
|
|
33
|
+
transportFactory: MemoryTransportFactory
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
assert(signalServer);
|
|
37
|
+
|
|
38
|
+
return new NetworkManager({
|
|
39
|
+
log: true,
|
|
40
|
+
signalManager: new WebsocketSignalManager([signalServer]),
|
|
41
|
+
transportFactory: createWebRTCTransportFactory({
|
|
42
|
+
iceServers: config.get('runtime.services.ice')
|
|
43
|
+
})
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type ClientServiceHostParams = {
|
|
48
|
+
config: Config;
|
|
49
|
+
signer?: HaloSigner;
|
|
50
|
+
modelFactory?: ModelFactory;
|
|
51
|
+
networkManager: NetworkManager;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Remote service implementation.
|
|
56
|
+
*/
|
|
57
|
+
export class ClientServiceHost implements ClientServiceProvider {
|
|
58
|
+
private readonly _config: Config;
|
|
59
|
+
private readonly _signer?: HaloSigner;
|
|
60
|
+
private readonly _context: ServiceContext;
|
|
61
|
+
private readonly _services: ClientServices;
|
|
62
|
+
|
|
63
|
+
constructor({
|
|
64
|
+
config,
|
|
65
|
+
modelFactory = new ModelFactory().registerModel(ObjectModel),
|
|
66
|
+
signer,
|
|
67
|
+
networkManager
|
|
68
|
+
}: ClientServiceHostParams) {
|
|
69
|
+
this._config = config;
|
|
70
|
+
this._signer = signer;
|
|
71
|
+
|
|
72
|
+
// TODO(dmaretskyi): Remove keyStorage.
|
|
73
|
+
const { storage } = createStorageObjects(this._config.get('runtime.client.storage', {})!);
|
|
74
|
+
this._context = new ServiceContext(storage, networkManager, modelFactory);
|
|
75
|
+
|
|
76
|
+
this._services = createServices({
|
|
77
|
+
config: this._config,
|
|
78
|
+
context: this._context,
|
|
79
|
+
signer: this._signer,
|
|
80
|
+
networkManager
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
get services() {
|
|
85
|
+
return this._services;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// TODO(dmaretskyi): progress.
|
|
89
|
+
async open(onProgressCallback?: ((progress: any) => void) | undefined) {
|
|
90
|
+
log('opening...');
|
|
91
|
+
await this._context.open();
|
|
92
|
+
// this._devtoolsEvents.ready.emit();
|
|
93
|
+
log('opened');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async close() {
|
|
97
|
+
log('closing...');
|
|
98
|
+
await this._context.close();
|
|
99
|
+
log('closed');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { schema } from '@dxos/protocols';
|
|
6
|
+
import { PartyService, ProfileService, SystemService, HaloService } from '@dxos/protocols/proto/dxos/client';
|
|
7
|
+
import { DevtoolsHost, TracingService } from '@dxos/protocols/proto/dxos/devtools/host';
|
|
8
|
+
import { DataService } from '@dxos/protocols/proto/dxos/echo/service';
|
|
9
|
+
import { createServiceBundle } from '@dxos/rpc';
|
|
10
|
+
|
|
11
|
+
// TODO(burdon): Is there a way to mark TS (generics) so cast isn't required for result of stream?
|
|
12
|
+
export type ClientServices = {
|
|
13
|
+
DataService: DataService;
|
|
14
|
+
HaloService: HaloService;
|
|
15
|
+
PartyService: PartyService;
|
|
16
|
+
ProfileService: ProfileService;
|
|
17
|
+
SystemService: SystemService;
|
|
18
|
+
|
|
19
|
+
DevtoolsHost: DevtoolsHost;
|
|
20
|
+
TracingService: TracingService;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// TODO(burdon): Rethink name/factory.
|
|
24
|
+
export const clientServiceBundle = createServiceBundle<ClientServices>({
|
|
25
|
+
DataService: schema.getService('dxos.echo.service.DataService'),
|
|
26
|
+
HaloService: schema.getService('dxos.client.HaloService'),
|
|
27
|
+
PartyService: schema.getService('dxos.client.PartyService'),
|
|
28
|
+
ProfileService: schema.getService('dxos.client.ProfileService'),
|
|
29
|
+
SystemService: schema.getService('dxos.client.SystemService'),
|
|
30
|
+
|
|
31
|
+
DevtoolsHost: schema.getService('dxos.devtools.host.DevtoolsHost'),
|
|
32
|
+
TracingService: schema.getService('dxos.devtools.host.TracingService')
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export interface ClientServiceProvider {
|
|
36
|
+
services: ClientServices;
|
|
37
|
+
open(onProgressCallback?: ((progress: any) => void) | undefined): Promise<void>;
|
|
38
|
+
close(): Promise<void>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { SignRequest, SignResponse } from '@dxos/protocols/proto/dxos/client';
|
|
6
|
+
import { KeyRecord } from '@dxos/protocols/proto/dxos/halo/keys';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
export interface HaloSigner {
|
|
12
|
+
sign: (request: SignRequest, key: KeyRecord) => Promise<SignResponse>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
|
|
7
|
+
import { MemorySignalManagerContext } from '@dxos/messaging';
|
|
8
|
+
import { afterTest } from '@dxos/testutils';
|
|
9
|
+
|
|
10
|
+
import { createServiceContext } from './testing';
|
|
11
|
+
|
|
12
|
+
describe('services/halo', function () {
|
|
13
|
+
it('creates identity', async function () {
|
|
14
|
+
const peer = await createServiceContext();
|
|
15
|
+
await peer.open();
|
|
16
|
+
afterTest(() => peer.close());
|
|
17
|
+
|
|
18
|
+
const identity = await peer.createIdentity();
|
|
19
|
+
expect(identity).not.to.be.undefined;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('device invitations', async function () {
|
|
23
|
+
const signalContext = new MemorySignalManagerContext();
|
|
24
|
+
|
|
25
|
+
const peer1 = await createServiceContext({ signalContext });
|
|
26
|
+
const peer2 = await createServiceContext({ signalContext });
|
|
27
|
+
await peer1.open();
|
|
28
|
+
await peer2.open();
|
|
29
|
+
afterTest(() => peer1.close());
|
|
30
|
+
afterTest(() => peer2.close());
|
|
31
|
+
|
|
32
|
+
const identity1 = await peer1.createIdentity();
|
|
33
|
+
expect(peer1.identityManager.identity).to.eq(identity1);
|
|
34
|
+
expect(peer2.identityManager.identity).to.be.undefined;
|
|
35
|
+
|
|
36
|
+
const invitation = await peer1.haloInvitations.createInvitation();
|
|
37
|
+
const identity2 = await peer2.haloInvitations.acceptInvitation(invitation);
|
|
38
|
+
expect(identity2.identityKey).to.deep.eq(identity1.identityKey);
|
|
39
|
+
});
|
|
40
|
+
});
|