@dxos/echo-db 2.33.1-dev.eb88db79 → 2.33.2-dev.0286e021
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/src/echo.d.ts +8 -8
- package/dist/src/echo.d.ts.map +1 -1
- package/dist/src/echo.js +15 -16
- package/dist/src/echo.js.map +1 -1
- package/dist/src/echo.test.js +7 -13
- package/dist/src/echo.test.js.map +1 -1
- package/dist/src/halo/contact-manager.d.ts +4 -4
- package/dist/src/halo/contact-manager.d.ts.map +1 -1
- package/dist/src/halo/contact-manager.js +10 -4
- package/dist/src/halo/contact-manager.js.map +1 -1
- package/dist/src/halo/halo-factory.d.ts +14 -9
- package/dist/src/halo/halo-factory.d.ts.map +1 -1
- package/dist/src/halo/halo-factory.js +47 -14
- package/dist/src/halo/halo-factory.js.map +1 -1
- package/dist/src/halo/halo-party.d.ts +35 -7
- package/dist/src/halo/halo-party.d.ts.map +1 -1
- package/dist/src/halo/halo-party.js +97 -18
- package/dist/src/halo/halo-party.js.map +1 -1
- package/dist/src/halo/halo.d.ts +14 -12
- package/dist/src/halo/halo.d.ts.map +1 -1
- package/dist/src/halo/halo.js +31 -29
- package/dist/src/halo/halo.js.map +1 -1
- package/dist/src/halo/halo.test.d.ts +2 -0
- package/dist/src/halo/halo.test.d.ts.map +1 -0
- package/dist/src/halo/halo.test.js +162 -0
- package/dist/src/halo/halo.test.js.map +1 -0
- package/dist/src/halo/identity-manager.d.ts +10 -8
- package/dist/src/halo/identity-manager.d.ts.map +1 -1
- package/dist/src/halo/identity-manager.js +24 -20
- package/dist/src/halo/identity-manager.js.map +1 -1
- package/dist/src/halo/identity.d.ts +12 -6
- package/dist/src/halo/identity.d.ts.map +1 -1
- package/dist/src/halo/identity.js +11 -15
- package/dist/src/halo/identity.js.map +1 -1
- package/dist/src/halo/index.d.ts +1 -1
- package/dist/src/halo/index.d.ts.map +1 -1
- package/dist/src/halo/index.js +1 -1
- package/dist/src/halo/index.js.map +1 -1
- package/dist/src/halo/party-opener.js +1 -1
- package/dist/src/halo/party-opener.js.map +1 -1
- package/dist/src/halo/preferences.d.ts +9 -9
- package/dist/src/halo/preferences.d.ts.map +1 -1
- package/dist/src/halo/preferences.js +25 -15
- package/dist/src/halo/preferences.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/invitations/greeting-initiator.d.ts +14 -4
- package/dist/src/invitations/greeting-initiator.d.ts.map +1 -1
- package/dist/src/invitations/greeting-initiator.js +15 -16
- package/dist/src/invitations/greeting-initiator.js.map +1 -1
- package/dist/src/invitations/greeting-responder.d.ts +3 -3
- package/dist/src/invitations/greeting-responder.d.ts.map +1 -1
- package/dist/src/invitations/greeting-responder.js +3 -4
- package/dist/src/invitations/greeting-responder.js.map +1 -1
- package/dist/src/invitations/halo-recovery-initiator.d.ts +6 -4
- package/dist/src/invitations/halo-recovery-initiator.d.ts.map +1 -1
- package/dist/src/invitations/halo-recovery-initiator.js +15 -26
- package/dist/src/invitations/halo-recovery-initiator.js.map +1 -1
- package/dist/src/invitations/index.d.ts +1 -1
- package/dist/src/invitations/index.js +1 -1
- package/dist/src/invitations/{invitation-manager.d.ts → invitation-factory.d.ts} +5 -6
- package/dist/src/invitations/invitation-factory.d.ts.map +1 -0
- package/dist/src/invitations/{invitation-manager.js → invitation-factory.js} +9 -15
- package/dist/src/invitations/invitation-factory.js.map +1 -0
- package/dist/src/invitations/offline-invitation-claimer.d.ts +3 -3
- package/dist/src/invitations/offline-invitation-claimer.js +1 -1
- package/dist/src/parties/{party-internal.d.ts → data-party.d.ts} +13 -11
- package/dist/src/parties/data-party.d.ts.map +1 -0
- package/dist/src/parties/{party-internal.js → data-party.js} +27 -37
- package/dist/src/parties/data-party.js.map +1 -0
- package/dist/src/parties/index.d.ts +1 -2
- package/dist/src/parties/index.d.ts.map +1 -1
- package/dist/src/parties/index.js +1 -2
- package/dist/src/parties/index.js.map +1 -1
- package/dist/src/parties/party-factory.d.ts +10 -11
- package/dist/src/parties/party-factory.d.ts.map +1 -1
- package/dist/src/parties/party-factory.js +25 -16
- package/dist/src/parties/party-factory.js.map +1 -1
- package/dist/src/parties/party-manager.d.ts +9 -9
- package/dist/src/parties/party-manager.d.ts.map +1 -1
- package/dist/src/parties/party-manager.js +13 -13
- package/dist/src/parties/party-manager.js.map +1 -1
- package/dist/src/parties/party-manager.test.js +69 -68
- package/dist/src/parties/party-manager.test.js.map +1 -1
- package/dist/src/{halo → parties}/party-preferences.d.ts +3 -3
- package/dist/src/parties/party-preferences.d.ts.map +1 -0
- package/dist/src/{halo → parties}/party-preferences.js +3 -3
- package/dist/src/parties/party-preferences.js.map +1 -0
- package/dist/src/pipeline/index.d.ts +3 -1
- package/dist/src/pipeline/index.d.ts.map +1 -1
- package/dist/src/pipeline/index.js +3 -1
- package/dist/src/pipeline/index.js.map +1 -1
- package/dist/src/{metadata → pipeline}/metadata-store.d.ts +0 -0
- package/dist/src/{metadata → pipeline}/metadata-store.d.ts.map +1 -1
- package/dist/src/{metadata → pipeline}/metadata-store.js +0 -0
- package/dist/src/{metadata → pipeline}/metadata-store.js.map +1 -1
- package/dist/src/{metadata → pipeline}/metadata-store.test.d.ts +0 -0
- package/dist/src/pipeline/metadata-store.test.d.ts.map +1 -0
- package/dist/src/{metadata → pipeline}/metadata-store.test.js +0 -0
- package/dist/src/{metadata → pipeline}/metadata-store.test.js.map +1 -1
- package/dist/src/{parties → pipeline}/party-core.d.ts +0 -0
- package/dist/src/pipeline/party-core.d.ts.map +1 -0
- package/dist/src/{parties → pipeline}/party-core.js +8 -4
- package/dist/src/pipeline/party-core.js.map +1 -0
- package/dist/src/{parties → pipeline}/party-core.test.d.ts +0 -0
- package/dist/src/pipeline/party-core.test.d.ts.map +1 -0
- package/dist/src/{parties → pipeline}/party-core.test.js +3 -4
- package/dist/src/pipeline/party-core.test.js.map +1 -0
- package/dist/src/pipeline/party-feed-provider.d.ts +3 -2
- package/dist/src/pipeline/party-feed-provider.d.ts.map +1 -1
- package/dist/src/pipeline/party-feed-provider.js +40 -19
- package/dist/src/pipeline/party-feed-provider.js.map +1 -1
- package/dist/src/protocol/auth-plugin.d.ts +7 -0
- package/dist/src/protocol/auth-plugin.d.ts.map +1 -0
- package/dist/src/protocol/auth-plugin.js +16 -0
- package/dist/src/protocol/auth-plugin.js.map +1 -0
- package/dist/src/protocol/authenticator.d.ts +14 -0
- package/dist/src/protocol/authenticator.d.ts.map +1 -0
- package/dist/src/protocol/authenticator.js +31 -0
- package/dist/src/protocol/authenticator.js.map +1 -0
- package/dist/src/protocol/authenticator.test.d.ts +2 -0
- package/dist/src/protocol/authenticator.test.d.ts.map +1 -0
- package/dist/src/protocol/authenticator.test.js +45 -0
- package/dist/src/protocol/authenticator.test.js.map +1 -0
- package/dist/src/protocol/credentials-signer.d.ts +31 -0
- package/dist/src/protocol/credentials-signer.d.ts.map +1 -0
- package/dist/src/protocol/credentials-signer.js +53 -0
- package/dist/src/protocol/credentials-signer.js.map +1 -0
- package/dist/src/protocol/halo-recovery-plugin.d.ts +10 -0
- package/dist/src/protocol/halo-recovery-plugin.d.ts.map +1 -0
- package/dist/src/protocol/halo-recovery-plugin.js +18 -0
- package/dist/src/protocol/halo-recovery-plugin.js.map +1 -0
- package/dist/src/protocol/index.d.ts +7 -0
- package/dist/src/protocol/index.d.ts.map +1 -0
- package/dist/src/{metadata → protocol}/index.js +7 -2
- package/dist/src/protocol/index.js.map +1 -0
- package/dist/src/protocol/offline-invitation-plugin.d.ts +9 -0
- package/dist/src/protocol/offline-invitation-plugin.d.ts.map +1 -0
- package/dist/src/protocol/offline-invitation-plugin.js +17 -0
- package/dist/src/protocol/offline-invitation-plugin.js.map +1 -0
- package/dist/src/{pipeline → protocol}/party-protocol-factory.d.ts +7 -17
- package/dist/src/protocol/party-protocol-factory.d.ts.map +1 -0
- package/dist/src/{pipeline → protocol}/party-protocol-factory.js +7 -47
- package/dist/src/protocol/party-protocol-factory.js.map +1 -0
- package/dist/src/snapshots/snapshot-generator.d.ts +1 -1
- package/dist/src/snapshots/snapshot-generator.d.ts.map +1 -1
- package/dist/src/testing/testing-factories.js.map +1 -1
- package/dist/src/testing/testing.d.ts +2 -2
- package/dist/src/testing/testing.d.ts.map +1 -1
- package/dist/src/testing/testing.js +1 -7
- package/dist/src/testing/testing.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +20 -20
- package/src/echo.test.ts +8 -14
- package/src/echo.ts +107 -109
- package/src/halo/contact-manager.ts +9 -5
- package/src/halo/halo-factory.ts +82 -19
- package/src/halo/halo-party.ts +137 -21
- package/src/halo/halo.test.ts +198 -0
- package/src/halo/halo.ts +40 -38
- package/src/halo/identity-manager.ts +33 -28
- package/src/halo/identity.ts +19 -23
- package/src/halo/index.ts +1 -1
- package/src/halo/party-opener.ts +1 -1
- package/src/halo/preferences.ts +29 -18
- package/src/index.ts +1 -1
- package/src/invitations/greeting-initiator.ts +37 -32
- package/src/invitations/greeting-responder.ts +4 -5
- package/src/invitations/halo-recovery-initiator.ts +16 -24
- package/src/invitations/index.ts +1 -1
- package/src/invitations/{invitation-manager.ts → invitation-factory.ts} +8 -15
- package/src/invitations/offline-invitation-claimer.ts +3 -3
- package/src/parties/{party-internal.ts → data-party.ts} +30 -55
- package/src/parties/index.ts +1 -2
- package/src/parties/party-factory.ts +31 -21
- package/src/parties/party-manager.test.ts +99 -73
- package/src/parties/party-manager.ts +17 -17
- package/src/{halo → parties}/party-preferences.ts +3 -3
- package/src/pipeline/index.ts +3 -1
- package/src/{metadata → pipeline}/metadata-store.test.ts +0 -0
- package/src/{metadata → pipeline}/metadata-store.ts +0 -0
- package/src/{parties → pipeline}/party-core.test.ts +1 -2
- package/src/{parties → pipeline}/party-core.ts +12 -7
- package/src/pipeline/party-feed-provider.ts +32 -21
- package/src/protocol/auth-plugin.ts +14 -0
- package/src/protocol/authenticator.test.ts +66 -0
- package/src/protocol/authenticator.ts +56 -0
- package/src/protocol/credentials-signer.ts +60 -0
- package/src/protocol/halo-recovery-plugin.ts +20 -0
- package/src/protocol/index.ts +10 -0
- package/src/protocol/offline-invitation-plugin.ts +19 -0
- package/src/{pipeline → protocol}/party-protocol-factory.ts +10 -82
- package/src/snapshots/snapshot-generator.ts +1 -1
- package/src/testing/testing-factories.ts +2 -2
- package/src/testing/testing.ts +3 -9
- package/dist/src/halo/party-preferences.d.ts.map +0 -1
- package/dist/src/halo/party-preferences.js.map +0 -1
- package/dist/src/invitations/invitation-manager.d.ts.map +0 -1
- package/dist/src/invitations/invitation-manager.js.map +0 -1
- package/dist/src/metadata/index.d.ts +0 -2
- package/dist/src/metadata/index.d.ts.map +0 -1
- package/dist/src/metadata/index.js.map +0 -1
- package/dist/src/metadata/metadata-store.test.d.ts.map +0 -1
- package/dist/src/parties/authenticator.d.ts +0 -5
- package/dist/src/parties/authenticator.d.ts.map +0 -1
- package/dist/src/parties/authenticator.js +0 -27
- package/dist/src/parties/authenticator.js.map +0 -1
- package/dist/src/parties/party-core.d.ts.map +0 -1
- package/dist/src/parties/party-core.js.map +0 -1
- package/dist/src/parties/party-core.test.d.ts.map +0 -1
- package/dist/src/parties/party-core.test.js.map +0 -1
- package/dist/src/parties/party-internal.d.ts.map +0 -1
- package/dist/src/parties/party-internal.js.map +0 -1
- package/dist/src/pipeline/party-protocol-factory.d.ts.map +0 -1
- package/dist/src/pipeline/party-protocol-factory.js.map +0 -1
- package/src/metadata/index.ts +0 -5
- package/src/parties/authenticator.ts +0 -31
package/src/halo/halo-party.ts
CHANGED
|
@@ -2,10 +2,22 @@
|
|
|
2
2
|
// Copyright 2020 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import assert from 'assert';
|
|
6
|
+
|
|
7
|
+
import { Event, synchronized } from '@dxos/async';
|
|
5
8
|
import { KeyHint } from '@dxos/credentials';
|
|
6
9
|
import { PublicKey } from '@dxos/crypto';
|
|
7
|
-
|
|
8
|
-
import {
|
|
10
|
+
import { timed } from '@dxos/debug';
|
|
11
|
+
import { Timeframe } from '@dxos/echo-protocol';
|
|
12
|
+
import { ModelFactory } from '@dxos/model-factory';
|
|
13
|
+
import { NetworkManager } from '@dxos/network-manager';
|
|
14
|
+
|
|
15
|
+
import { InvitationAuthenticator, InvitationDescriptor, InvitationFactory, InvitationOptions } from '../invitations';
|
|
16
|
+
import { PARTY_ITEM_TYPE } from '../parties';
|
|
17
|
+
import { PartyFeedProvider, PartyProtocolFactory, PartyCore, PartyOptions } from '../pipeline';
|
|
18
|
+
import { createAuthenticator, createAuthPlugin, createCredentialsProvider, createHaloRecoveryPlugin } from '../protocol';
|
|
19
|
+
import { CredentialsSigner } from '../protocol/credentials-signer';
|
|
20
|
+
import { SnapshotStore } from '../snapshots';
|
|
9
21
|
import { ContactManager } from './contact-manager';
|
|
10
22
|
import { Preferences } from './preferences';
|
|
11
23
|
|
|
@@ -23,23 +35,56 @@ export interface JoinedParty {
|
|
|
23
35
|
}
|
|
24
36
|
|
|
25
37
|
/**
|
|
26
|
-
*
|
|
38
|
+
* Provides all HALO-related functionality.
|
|
27
39
|
*/
|
|
28
40
|
export class HaloParty {
|
|
41
|
+
public readonly update = new Event<void>();
|
|
42
|
+
|
|
43
|
+
private readonly _partyCore: PartyCore;
|
|
44
|
+
private _invitationManager?: InvitationFactory;
|
|
45
|
+
private _protocol?: PartyProtocolFactory;
|
|
46
|
+
|
|
29
47
|
private readonly _contactManager: ContactManager;
|
|
30
48
|
private readonly _preferences: Preferences;
|
|
31
49
|
|
|
32
50
|
constructor (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
51
|
+
modelFactory: ModelFactory,
|
|
52
|
+
snapshotStore: SnapshotStore,
|
|
53
|
+
private readonly _feedProvider: PartyFeedProvider,
|
|
54
|
+
private readonly _credentialsSigner: CredentialsSigner,
|
|
55
|
+
private readonly _networkManager: NetworkManager,
|
|
56
|
+
private readonly _hints: KeyHint[] = [],
|
|
57
|
+
_initialTimeframe: Timeframe | undefined,
|
|
58
|
+
_options: PartyOptions
|
|
36
59
|
) {
|
|
37
|
-
this.
|
|
38
|
-
|
|
60
|
+
this._partyCore = new PartyCore(
|
|
61
|
+
_credentialsSigner.getIdentityKey().publicKey,
|
|
62
|
+
_feedProvider,
|
|
63
|
+
modelFactory,
|
|
64
|
+
snapshotStore,
|
|
65
|
+
_credentialsSigner.getIdentityKey().publicKey,
|
|
66
|
+
_initialTimeframe,
|
|
67
|
+
_options
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
this._contactManager = new ContactManager(() => this.isOpen ? this.database : undefined);
|
|
71
|
+
this._preferences = new Preferences(
|
|
72
|
+
() => this.isOpen ? this.database : undefined,
|
|
73
|
+
_credentialsSigner.getDeviceKey().publicKey
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Party key.
|
|
79
|
+
* Always equal to the identity key.
|
|
80
|
+
* @deprecated Should remove.
|
|
81
|
+
*/
|
|
82
|
+
get key () {
|
|
83
|
+
return this._partyCore.key;
|
|
39
84
|
}
|
|
40
85
|
|
|
41
86
|
get isOpen () {
|
|
42
|
-
return this.
|
|
87
|
+
return !!(this._partyCore.isOpen && this._protocol);
|
|
43
88
|
}
|
|
44
89
|
|
|
45
90
|
get contacts () {
|
|
@@ -52,7 +97,7 @@ export class HaloParty {
|
|
|
52
97
|
|
|
53
98
|
// TODO(burdon): Remove.
|
|
54
99
|
get database () {
|
|
55
|
-
return this.
|
|
100
|
+
return this._partyCore.database;
|
|
56
101
|
}
|
|
57
102
|
|
|
58
103
|
//
|
|
@@ -60,32 +105,103 @@ export class HaloParty {
|
|
|
60
105
|
// (eg, identity, credentials, device management.)
|
|
61
106
|
//
|
|
62
107
|
|
|
63
|
-
get invitationManager () {
|
|
64
|
-
return this._party.invitationManager;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
108
|
get identityInfo () {
|
|
68
|
-
return this.
|
|
109
|
+
return this._partyCore.processor.infoMessages.get(this._credentialsSigner.getIdentityKey().publicKey.toHex());
|
|
69
110
|
}
|
|
70
111
|
|
|
71
112
|
get identityGenesis () {
|
|
72
|
-
return this.
|
|
113
|
+
return this._partyCore.processor.credentialMessages.get(this._credentialsSigner.getIdentityKey().publicKey.toHex());
|
|
73
114
|
}
|
|
74
115
|
|
|
75
116
|
get memberKeys () {
|
|
76
|
-
return this.
|
|
117
|
+
return this._partyCore.processor.memberKeys;
|
|
77
118
|
}
|
|
78
119
|
|
|
79
120
|
get credentialMessages () {
|
|
80
|
-
return this.
|
|
121
|
+
return this._partyCore.processor.credentialMessages;
|
|
81
122
|
}
|
|
82
123
|
|
|
83
124
|
get feedKeys () {
|
|
84
|
-
return this.
|
|
125
|
+
return this._partyCore.processor.feedKeys;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async getWriteFeedKey () {
|
|
129
|
+
const feed = await this._feedProvider.createOrOpenWritableFeed();
|
|
130
|
+
return feed.key;
|
|
85
131
|
}
|
|
86
132
|
|
|
87
|
-
|
|
133
|
+
get processor () {
|
|
134
|
+
return this._partyCore.processor;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Opens the pipeline and connects the streams.
|
|
139
|
+
*/
|
|
140
|
+
@synchronized
|
|
141
|
+
@timed(5_000)
|
|
142
|
+
async open () {
|
|
143
|
+
if (this.isOpen) {
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
await this._partyCore.open(this._hints);
|
|
148
|
+
|
|
149
|
+
this._invitationManager = new InvitationFactory(
|
|
150
|
+
this._partyCore.processor,
|
|
151
|
+
this._credentialsSigner,
|
|
152
|
+
this._networkManager
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
//
|
|
156
|
+
// Network/swarm.
|
|
157
|
+
//
|
|
158
|
+
|
|
159
|
+
const writeFeed = await this._partyCore.getWriteFeed();
|
|
160
|
+
const peerId = this._credentialsSigner.getDeviceKey().publicKey;
|
|
161
|
+
this._protocol = new PartyProtocolFactory(
|
|
162
|
+
this._partyCore.key,
|
|
163
|
+
this._networkManager,
|
|
164
|
+
this._feedProvider,
|
|
165
|
+
peerId,
|
|
166
|
+
createCredentialsProvider(this._credentialsSigner, this._partyCore.key, writeFeed.key),
|
|
167
|
+
this._partyCore.processor.getActiveFeedSet()
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Replication.
|
|
171
|
+
await this._protocol.start([
|
|
172
|
+
createAuthPlugin(createAuthenticator(this._partyCore.processor, this._credentialsSigner), peerId),
|
|
173
|
+
createHaloRecoveryPlugin(this._credentialsSigner.getIdentityKey().publicKey, this._invitationManager, peerId)
|
|
174
|
+
]);
|
|
175
|
+
|
|
176
|
+
// Issue an 'update' whenever the properties change.
|
|
177
|
+
this.database.select({ type: PARTY_ITEM_TYPE }).exec().update.on(() => this.update.emit());
|
|
178
|
+
|
|
179
|
+
this.update.emit();
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Closes the pipeline and streams.
|
|
185
|
+
*/
|
|
186
|
+
@synchronized
|
|
88
187
|
async close () {
|
|
89
|
-
|
|
188
|
+
if (!this.isOpen) {
|
|
189
|
+
return this;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
await this._partyCore.close();
|
|
193
|
+
await this._protocol?.stop();
|
|
194
|
+
|
|
195
|
+
this._protocol = undefined;
|
|
196
|
+
this._invitationManager = undefined;
|
|
197
|
+
|
|
198
|
+
this.update.emit();
|
|
199
|
+
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async createInvitation (authenticationDetails: InvitationAuthenticator, options?: InvitationOptions): Promise<InvitationDescriptor> {
|
|
204
|
+
assert(this._invitationManager, 'HALO party not open.');
|
|
205
|
+
return this._invitationManager.createInvitation(authenticationDetails, options);
|
|
90
206
|
}
|
|
91
207
|
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import expect from 'expect';
|
|
6
|
+
import { it as test } from 'mocha';
|
|
7
|
+
|
|
8
|
+
import { defaultSecretProvider, Keyring } from '@dxos/credentials';
|
|
9
|
+
import { generateSeedPhrase, keyPairFromSeedPhrase, PublicKey } from '@dxos/crypto';
|
|
10
|
+
import { codec } from '@dxos/echo-protocol';
|
|
11
|
+
import { FeedStore } from '@dxos/feed-store';
|
|
12
|
+
import { ModelFactory } from '@dxos/model-factory';
|
|
13
|
+
import { NetworkManager } from '@dxos/network-manager';
|
|
14
|
+
import { ObjectModel } from '@dxos/object-model';
|
|
15
|
+
import { afterTest, testTimeout } from '@dxos/testutils';
|
|
16
|
+
|
|
17
|
+
import { defaultInvitationAuthenticator } from '../invitations';
|
|
18
|
+
import { MetadataStore, PartyFeedProvider } from '../pipeline';
|
|
19
|
+
import { SnapshotStore } from '../snapshots';
|
|
20
|
+
import { createRamStorage } from '../util';
|
|
21
|
+
import { HALO } from './halo';
|
|
22
|
+
|
|
23
|
+
describe('HALO', () => {
|
|
24
|
+
const setup = () => {
|
|
25
|
+
const modelFactory = new ModelFactory()
|
|
26
|
+
.registerModel(ObjectModel);
|
|
27
|
+
|
|
28
|
+
const networkManager = new NetworkManager();
|
|
29
|
+
const snapshotStore = new SnapshotStore(createRamStorage());
|
|
30
|
+
const metadataStore = new MetadataStore(createRamStorage());
|
|
31
|
+
const keyring = new Keyring();
|
|
32
|
+
const feedStore = new FeedStore(createRamStorage(), { valueEncoding: codec });
|
|
33
|
+
|
|
34
|
+
const feedProviderFactory = (partyKey: PublicKey) => new PartyFeedProvider(
|
|
35
|
+
metadataStore,
|
|
36
|
+
keyring,
|
|
37
|
+
feedStore,
|
|
38
|
+
partyKey
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return new HALO({
|
|
42
|
+
keyring: keyring,
|
|
43
|
+
networkManager: networkManager,
|
|
44
|
+
metadataStore: metadataStore,
|
|
45
|
+
feedProviderFactory,
|
|
46
|
+
modelFactory: modelFactory,
|
|
47
|
+
snapshotStore: snapshotStore,
|
|
48
|
+
options: {}
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const setupOpen = async () => {
|
|
53
|
+
const halo = setup();
|
|
54
|
+
await halo.open();
|
|
55
|
+
afterTest(() => halo.close());
|
|
56
|
+
return halo;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
test('open & close', async () => {
|
|
60
|
+
const halo = setup();
|
|
61
|
+
|
|
62
|
+
await halo.open();
|
|
63
|
+
await halo.close();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('create profile', async () => {
|
|
67
|
+
const halo = setup();
|
|
68
|
+
await halo.open();
|
|
69
|
+
afterTest(() => halo.close());
|
|
70
|
+
|
|
71
|
+
const profile = await halo.createProfile({ username: 'Test user' });
|
|
72
|
+
expect(profile.username).toEqual('Test user');
|
|
73
|
+
expect(profile.publicKey).toBeInstanceOf(PublicKey);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('reload with profile', async () => {
|
|
77
|
+
const halo = setup();
|
|
78
|
+
await halo.open();
|
|
79
|
+
|
|
80
|
+
const initialProfile = await halo.createProfile({ username: 'Test user' });
|
|
81
|
+
await halo.close();
|
|
82
|
+
|
|
83
|
+
await halo.open();
|
|
84
|
+
const profile = halo.getProfile();
|
|
85
|
+
expect(profile).toBeDefined();
|
|
86
|
+
expect(profile!.username).toEqual('Test user');
|
|
87
|
+
expect(profile!.publicKey.equals(initialProfile.publicKey)).toBeTruthy();
|
|
88
|
+
|
|
89
|
+
await halo.close();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('invite another device', async () => {
|
|
93
|
+
const deviceA = setup();
|
|
94
|
+
await deviceA.open();
|
|
95
|
+
afterTest(() => deviceA.close());
|
|
96
|
+
const profileA = await deviceA.createProfile({ username: 'Test user' });
|
|
97
|
+
|
|
98
|
+
const deviceB = setup();
|
|
99
|
+
await deviceB.open();
|
|
100
|
+
afterTest(() => deviceB.close());
|
|
101
|
+
|
|
102
|
+
const invitation = await deviceA.createInvitation(defaultInvitationAuthenticator);
|
|
103
|
+
await deviceB.join(invitation, defaultSecretProvider);
|
|
104
|
+
|
|
105
|
+
const profileB = deviceB.getProfile();
|
|
106
|
+
expect(profileB).toBeDefined();
|
|
107
|
+
expect(profileB!.username).toEqual('Test user');
|
|
108
|
+
expect(profileB!.publicKey.equals(profileA.publicKey)).toBeTruthy();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('invite 2 devices in a chain', async () => {
|
|
112
|
+
const deviceA = await setupOpen();
|
|
113
|
+
const deviceB = await setupOpen();
|
|
114
|
+
const deviceC = await setupOpen();
|
|
115
|
+
|
|
116
|
+
const profileA = await deviceA.createProfile({ username: 'Test user' });
|
|
117
|
+
|
|
118
|
+
{
|
|
119
|
+
const invitation = await deviceA.createInvitation(defaultInvitationAuthenticator);
|
|
120
|
+
await deviceB.join(invitation, defaultSecretProvider);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
{
|
|
124
|
+
const invitation = await deviceB.createInvitation(defaultInvitationAuthenticator);
|
|
125
|
+
await deviceC.join(invitation, defaultSecretProvider);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const profileC = deviceC.getProfile();
|
|
129
|
+
expect(profileC).toBeDefined();
|
|
130
|
+
expect(profileC!.username).toEqual('Test user');
|
|
131
|
+
expect(profileC!.publicKey.equals(profileA.publicKey)).toBeTruthy();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('recover HALO', async () => {
|
|
135
|
+
const deviceA = await setupOpen();
|
|
136
|
+
const deviceB = await setupOpen();
|
|
137
|
+
|
|
138
|
+
const seedPhrase = generateSeedPhrase();
|
|
139
|
+
const profileA = await deviceA.createProfile({ username: 'Test user', ...keyPairFromSeedPhrase(seedPhrase) });
|
|
140
|
+
|
|
141
|
+
await deviceB.recover(seedPhrase);
|
|
142
|
+
const profileB = deviceB.getProfile();
|
|
143
|
+
expect(profileB).toBeDefined();
|
|
144
|
+
expect(profileB!.username).toEqual('Test user');
|
|
145
|
+
expect(profileB!.publicKey.equals(profileA.publicKey)).toBeTruthy();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('HALO database is synced between 2 devices', async () => {
|
|
149
|
+
const deviceA = await setupOpen();
|
|
150
|
+
const deviceB = await setupOpen();
|
|
151
|
+
await deviceA.createProfile({ username: 'Test user' });
|
|
152
|
+
const invitation = await deviceA.createInvitation(defaultInvitationAuthenticator);
|
|
153
|
+
await deviceB.join(invitation, defaultSecretProvider);
|
|
154
|
+
|
|
155
|
+
{
|
|
156
|
+
const itemA = await deviceA.identity!.halo!.database.createItem({ type: 'example:test-1' });
|
|
157
|
+
const itemB = await testTimeout(deviceB.identity!.halo!.database.waitForItem({ type: 'example:test-1' }));
|
|
158
|
+
expect(itemA.id).toEqual(itemB.id);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
{
|
|
162
|
+
const itemB = await deviceB.identity!.halo.database.createItem({ type: 'example:test-2' });
|
|
163
|
+
const itemA = await testTimeout(deviceA.identity!.halo.database.waitForItem({ type: 'example:test-2' }));
|
|
164
|
+
expect(itemB.id).toEqual(itemA.id);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe('Preferences', () => {
|
|
169
|
+
test('global and device work on single device', async () => {
|
|
170
|
+
const halo = await setupOpen();
|
|
171
|
+
await halo.createProfile();
|
|
172
|
+
|
|
173
|
+
const globalPreferences: ObjectModel = halo.identity!.preferences!.getGlobalPreferences()!.model;
|
|
174
|
+
await globalPreferences.set('key', 'value');
|
|
175
|
+
expect(globalPreferences.get('key')).toEqual('value');
|
|
176
|
+
|
|
177
|
+
const devicePreferences: ObjectModel = halo.identity!.preferences!.getDevicePreferences()!.model;
|
|
178
|
+
await devicePreferences.set('key', 'value2');
|
|
179
|
+
expect(devicePreferences.get('key')).toEqual('value2');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('global preferences are synced between devices', async () => {
|
|
183
|
+
const deviceA = await setupOpen();
|
|
184
|
+
const deviceB = await setupOpen();
|
|
185
|
+
await deviceA.createProfile({ username: 'Test user' });
|
|
186
|
+
const invitation = await deviceA.createInvitation(defaultInvitationAuthenticator);
|
|
187
|
+
await deviceB.join(invitation, defaultSecretProvider);
|
|
188
|
+
|
|
189
|
+
const preferencesA: ObjectModel = deviceA.identity!.preferences!.getGlobalPreferences()!.model;
|
|
190
|
+
const preferencesB: ObjectModel = deviceB.identity!.preferences!.getGlobalPreferences()!.model;
|
|
191
|
+
|
|
192
|
+
const update = preferencesB.update.waitForCount(1);
|
|
193
|
+
await preferencesA.set('key', 'value');
|
|
194
|
+
await testTimeout(update);
|
|
195
|
+
expect(preferencesB.get('key')).toEqual('value');
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|
package/src/halo/halo.ts
CHANGED
|
@@ -8,12 +8,15 @@ import debug from 'debug';
|
|
|
8
8
|
import { synchronized } from '@dxos/async';
|
|
9
9
|
import { KeyRecord, Keyring, KeyType, SecretProvider } from '@dxos/credentials';
|
|
10
10
|
import { createKeyPair, KeyPair, PublicKey } from '@dxos/crypto';
|
|
11
|
+
import { raise } from '@dxos/debug';
|
|
12
|
+
import { ModelFactory } from '@dxos/model-factory';
|
|
11
13
|
import { NetworkManager } from '@dxos/network-manager';
|
|
12
14
|
|
|
13
15
|
import { ResultSet } from '../api';
|
|
14
16
|
import { InvitationAuthenticator, InvitationDescriptor, InvitationOptions } from '../invitations';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
+
import { OpenProgress } from '../parties';
|
|
18
|
+
import { MetadataStore, PartyOptions, PartyFeedProvider } from '../pipeline';
|
|
19
|
+
import { SnapshotStore } from '../snapshots';
|
|
17
20
|
import { Contact } from './contact-manager';
|
|
18
21
|
import { HaloFactory } from './halo-factory';
|
|
19
22
|
import { IdentityManager } from './identity-manager';
|
|
@@ -28,10 +31,12 @@ export interface ProfileInfo {
|
|
|
28
31
|
|
|
29
32
|
export interface HaloConfiguration {
|
|
30
33
|
keyring: Keyring,
|
|
31
|
-
partyFactory: PartyFactory,
|
|
32
34
|
networkManager: NetworkManager,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
metadataStore: MetadataStore,
|
|
36
|
+
modelFactory: ModelFactory,
|
|
37
|
+
snapshotStore: SnapshotStore,
|
|
38
|
+
feedProviderFactory: (partyKey: PublicKey) => PartyFeedProvider,
|
|
39
|
+
options: PartyOptions
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
/**
|
|
@@ -39,23 +44,26 @@ export interface HaloConfiguration {
|
|
|
39
44
|
*/
|
|
40
45
|
export class HALO {
|
|
41
46
|
private readonly _keyring: Keyring;
|
|
42
|
-
private readonly _partyManager: PartyManager;
|
|
43
47
|
private readonly _identityManager: IdentityManager;
|
|
44
48
|
|
|
45
49
|
constructor ({
|
|
46
50
|
keyring,
|
|
47
|
-
partyManager,
|
|
48
|
-
partyFactory,
|
|
49
51
|
networkManager,
|
|
50
|
-
metadataStore
|
|
52
|
+
metadataStore,
|
|
53
|
+
modelFactory,
|
|
54
|
+
snapshotStore,
|
|
55
|
+
feedProviderFactory,
|
|
56
|
+
options
|
|
51
57
|
}: HaloConfiguration) {
|
|
52
58
|
this._keyring = keyring;
|
|
53
|
-
this._partyManager = partyManager;
|
|
54
59
|
|
|
55
60
|
const haloFactory = new HaloFactory(
|
|
56
|
-
partyFactory,
|
|
57
61
|
networkManager,
|
|
58
|
-
|
|
62
|
+
modelFactory,
|
|
63
|
+
snapshotStore,
|
|
64
|
+
feedProviderFactory,
|
|
65
|
+
keyring,
|
|
66
|
+
options
|
|
59
67
|
);
|
|
60
68
|
|
|
61
69
|
this._identityManager = new IdentityManager(this._keyring, haloFactory, metadataStore);
|
|
@@ -98,7 +106,7 @@ export class HALO {
|
|
|
98
106
|
* User's IDENTITY keypair.
|
|
99
107
|
*/
|
|
100
108
|
get identityKey (): KeyRecord | undefined {
|
|
101
|
-
return this.identity
|
|
109
|
+
return this.identity?.identityKey;
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
/**
|
|
@@ -106,7 +114,7 @@ export class HALO {
|
|
|
106
114
|
*/
|
|
107
115
|
// TODO(burdon): Rename username (here and in data structure).
|
|
108
116
|
get identityDisplayName (): string | undefined {
|
|
109
|
-
return this.identity
|
|
117
|
+
return this.identity?.displayName;
|
|
110
118
|
}
|
|
111
119
|
|
|
112
120
|
/**
|
|
@@ -135,8 +143,7 @@ export class HALO {
|
|
|
135
143
|
* Closes HALO. Automatically called when client is destroyed.
|
|
136
144
|
*/
|
|
137
145
|
async close () {
|
|
138
|
-
|
|
139
|
-
await this.identity.halo?.close();
|
|
146
|
+
await this._identityManager.close();
|
|
140
147
|
}
|
|
141
148
|
|
|
142
149
|
/**
|
|
@@ -155,13 +162,12 @@ export class HALO {
|
|
|
155
162
|
*
|
|
156
163
|
* NOTE: This method does not initialize the HALO party.
|
|
157
164
|
*/
|
|
158
|
-
|
|
159
|
-
async createIdentity (keyPair: KeyPair) {
|
|
165
|
+
private async _createIdentityKeypair (keyPair: KeyPair) {
|
|
160
166
|
const { publicKey, secretKey } = keyPair;
|
|
161
167
|
assert(publicKey, 'Invalid publicKey');
|
|
162
168
|
assert(secretKey, 'Invalid secretKey');
|
|
163
169
|
|
|
164
|
-
if (this.identity
|
|
170
|
+
if (this.identity?.identityKey) {
|
|
165
171
|
// TODO(burdon): Bad API: Semantics change based on options.
|
|
166
172
|
// TODO(burdon): `createProfile` isn't part of this package.
|
|
167
173
|
throw new Error('Identity key already exists. Call createProfile without a keypair to only create a halo party.');
|
|
@@ -173,28 +179,25 @@ export class HALO {
|
|
|
173
179
|
/**
|
|
174
180
|
* Creates the initial HALO party.
|
|
175
181
|
*/
|
|
176
|
-
|
|
177
|
-
async create (displayName?: string) {
|
|
182
|
+
private async _createHaloParty (displayName?: string) {
|
|
178
183
|
// TODO(burdon): Why not assert?
|
|
179
|
-
if (this.identity
|
|
180
|
-
throw new Error('
|
|
181
|
-
}
|
|
182
|
-
if (!this.identity.identityKey) {
|
|
183
|
-
throw new Error('Cannot create HALO. Identity key not found.');
|
|
184
|
+
if (this.identity) {
|
|
185
|
+
throw new Error('Identity already initialized');
|
|
184
186
|
}
|
|
185
187
|
|
|
188
|
+
const identityKey = this._identityManager.getIdentityKey() ?? raise(new Error('Cannot create HALO. Identity key not found.'));
|
|
186
189
|
await this._identityManager.createHalo({
|
|
187
|
-
identityDisplayName: displayName ||
|
|
190
|
+
identityDisplayName: displayName || identityKey.publicKey.humanize()
|
|
188
191
|
});
|
|
189
192
|
}
|
|
190
193
|
|
|
191
194
|
/**
|
|
192
195
|
* Joins an existing identity HALO from a recovery seed phrase.
|
|
193
196
|
*/
|
|
197
|
+
// TODO(dmaretskyi): Do not return HALO party here.
|
|
194
198
|
async recover (seedPhrase: string) {
|
|
195
|
-
assert(this.
|
|
196
|
-
assert(!this.identity
|
|
197
|
-
assert(!this.identity.identityKey, 'Identity key already exists.');
|
|
199
|
+
assert(!this.identity?.halo, 'HALO already exists.');
|
|
200
|
+
assert(!this.identity?.identityKey, 'Identity key already exists.');
|
|
198
201
|
|
|
199
202
|
return this._identityManager.recoverHalo(seedPhrase);
|
|
200
203
|
}
|
|
@@ -202,9 +205,9 @@ export class HALO {
|
|
|
202
205
|
/**
|
|
203
206
|
* Joins an existing identity HALO by invitation.
|
|
204
207
|
*/
|
|
208
|
+
// TODO(dmaretskyi): Do not return HALO party here.
|
|
205
209
|
async join (invitationDescriptor: InvitationDescriptor, secretProvider: SecretProvider) {
|
|
206
|
-
assert(this.
|
|
207
|
-
assert(!this.identity.halo, 'HALO already exists.');
|
|
210
|
+
assert(!this.identity?.halo, 'HALO already exists.');
|
|
208
211
|
|
|
209
212
|
return this._identityManager.joinHalo(invitationDescriptor, secretProvider);
|
|
210
213
|
}
|
|
@@ -213,9 +216,8 @@ export class HALO {
|
|
|
213
216
|
* Create an invitation to an exiting identity HALO.
|
|
214
217
|
*/
|
|
215
218
|
async createInvitation (authenticationDetails: InvitationAuthenticator, options?: InvitationOptions) {
|
|
216
|
-
assert(this.identity
|
|
217
|
-
|
|
218
|
-
return this.identity.halo.invitationManager.createInvitation(authenticationDetails, options);
|
|
219
|
+
assert(this.identity?.halo, 'HALO not initialized.');
|
|
220
|
+
return this.identity.halo.createInvitation(authenticationDetails, options);
|
|
219
221
|
}
|
|
220
222
|
|
|
221
223
|
/**
|
|
@@ -223,7 +225,7 @@ export class HALO {
|
|
|
223
225
|
*/
|
|
224
226
|
// TODO(burdon): Expose ContactManager directly.
|
|
225
227
|
queryContacts (): ResultSet<Contact> {
|
|
226
|
-
assert(this.identity
|
|
228
|
+
assert(this.identity?.contacts, 'HALO not initialized.');
|
|
227
229
|
|
|
228
230
|
return this.identity.contacts.queryContacts();
|
|
229
231
|
}
|
|
@@ -245,8 +247,8 @@ export class HALO {
|
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
const keyPair = publicKey ? { publicKey: Buffer.from(publicKey), secretKey: Buffer.from(secretKey!) } : createKeyPair();
|
|
248
|
-
await this.
|
|
249
|
-
await this.
|
|
250
|
+
await this._createIdentityKeypair(keyPair);
|
|
251
|
+
await this._createHaloParty(username);
|
|
250
252
|
|
|
251
253
|
const profile = this.getProfile();
|
|
252
254
|
assert(profile);
|