@dxos/echo-db 2.33.5-dev.c37556a4 → 2.33.5-dev.c6e30fbc
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/api/index.js +5 -1
- package/dist/src/api/index.js.map +1 -1
- package/dist/src/echo.d.ts.map +1 -1
- package/dist/src/echo.js +1 -1
- package/dist/src/echo.js.map +1 -1
- package/dist/src/echo.test.js +12 -4
- package/dist/src/echo.test.js.map +1 -1
- package/dist/src/halo/halo-factory.d.ts +4 -4
- package/dist/src/halo/halo-factory.d.ts.map +1 -1
- package/dist/src/halo/halo-factory.js +3 -2
- package/dist/src/halo/halo-factory.js.map +1 -1
- package/dist/src/halo/halo-party.d.ts +4 -3
- package/dist/src/halo/halo-party.d.ts.map +1 -1
- package/dist/src/halo/halo-party.js +8 -4
- package/dist/src/halo/halo-party.js.map +1 -1
- package/dist/src/halo/halo.d.ts +2 -2
- package/dist/src/halo/halo.d.ts.map +1 -1
- package/dist/src/halo/index.js +5 -1
- package/dist/src/halo/index.js.map +1 -1
- package/dist/src/halo/party-opener.d.ts.map +1 -1
- package/dist/src/halo/party-opener.js +3 -1
- package/dist/src/halo/party-opener.js.map +1 -1
- package/dist/src/index.js +5 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/invitations/greeting-initiator.d.ts +4 -5
- package/dist/src/invitations/greeting-initiator.d.ts.map +1 -1
- package/dist/src/invitations/greeting-initiator.js +4 -4
- package/dist/src/invitations/greeting-initiator.js.map +1 -1
- package/dist/src/invitations/halo-recovery-initiator.d.ts +1 -1
- package/dist/src/invitations/halo-recovery-initiator.d.ts.map +1 -1
- package/dist/src/invitations/halo-recovery-initiator.js +1 -1
- package/dist/src/invitations/halo-recovery-initiator.js.map +1 -1
- package/dist/src/invitations/index.js +5 -1
- package/dist/src/invitations/index.js.map +1 -1
- package/dist/src/invitations/invitation-descriptor.js +5 -1
- package/dist/src/invitations/invitation-descriptor.js.map +1 -1
- package/dist/src/invitations/offline-invitation-claimer.js +1 -1
- package/dist/src/invitations/offline-invitation-claimer.js.map +1 -1
- package/dist/src/packlets/database/data-mirror.test.js +2 -2
- package/dist/src/packlets/database/data-mirror.test.js.map +1 -1
- package/dist/src/packlets/database/database-backend.d.ts +4 -5
- package/dist/src/packlets/database/database-backend.d.ts.map +1 -1
- package/dist/src/packlets/database/database-backend.js +5 -6
- package/dist/src/packlets/database/database-backend.js.map +1 -1
- package/dist/src/packlets/database/index.js +5 -1
- package/dist/src/packlets/database/index.js.map +1 -1
- package/dist/src/packlets/database/item-demuxer.d.ts +2 -2
- package/dist/src/packlets/database/item-demuxer.d.ts.map +1 -1
- package/dist/src/packlets/database/item-demuxer.js +2 -3
- package/dist/src/packlets/database/item-demuxer.js.map +1 -1
- package/dist/src/packlets/database/item-demuxer.test.js +13 -9
- package/dist/src/packlets/database/item-demuxer.test.js.map +1 -1
- package/dist/src/packlets/database/selection/index.js +5 -1
- package/dist/src/packlets/database/selection/index.js.map +1 -1
- package/dist/src/packlets/database/testing.d.ts.map +1 -1
- package/dist/src/packlets/database/testing.js +3 -4
- package/dist/src/packlets/database/testing.js.map +1 -1
- package/dist/src/packlets/database/timeframe-clock.d.ts +1 -0
- package/dist/src/packlets/database/timeframe-clock.d.ts.map +1 -1
- package/dist/src/packlets/database/timeframe-clock.js +3 -0
- package/dist/src/packlets/database/timeframe-clock.js.map +1 -1
- package/dist/src/parties/data-party.d.ts +6 -5
- package/dist/src/parties/data-party.d.ts.map +1 -1
- package/dist/src/parties/data-party.js +26 -5
- package/dist/src/parties/data-party.js.map +1 -1
- package/dist/src/parties/data-party.test.js +24 -5
- package/dist/src/parties/data-party.test.js.map +1 -1
- package/dist/src/parties/index.js +5 -1
- package/dist/src/parties/index.js.map +1 -1
- package/dist/src/parties/party-factory.d.ts +5 -4
- package/dist/src/parties/party-factory.d.ts.map +1 -1
- package/dist/src/parties/party-factory.js +15 -11
- package/dist/src/parties/party-factory.js.map +1 -1
- package/dist/src/parties/party-manager.d.ts +3 -4
- package/dist/src/parties/party-manager.d.ts.map +1 -1
- package/dist/src/parties/party-manager.js +8 -7
- package/dist/src/parties/party-manager.js.map +1 -1
- package/dist/src/parties/party-manager.test.js +9 -9
- package/dist/src/parties/party-manager.test.js.map +1 -1
- package/dist/src/pipeline/{pipeline.d.ts → feed-muxer.d.ts} +8 -11
- package/dist/src/pipeline/feed-muxer.d.ts.map +1 -0
- package/dist/src/pipeline/{pipeline.js → feed-muxer.js} +34 -41
- package/dist/src/pipeline/feed-muxer.js.map +1 -0
- package/dist/src/pipeline/feed-muxer.test.d.ts +2 -0
- package/dist/src/pipeline/feed-muxer.test.d.ts.map +1 -0
- package/dist/src/pipeline/{pipeline.test.js → feed-muxer.test.js} +17 -15
- package/dist/src/pipeline/feed-muxer.test.js.map +1 -0
- package/dist/src/pipeline/index.d.ts +2 -2
- package/dist/src/pipeline/index.d.ts.map +1 -1
- package/dist/src/pipeline/index.js +7 -3
- package/dist/src/pipeline/index.js.map +1 -1
- package/dist/src/pipeline/message-selector.d.ts.map +1 -1
- package/dist/src/pipeline/message-selector.js +6 -5
- package/dist/src/pipeline/message-selector.js.map +1 -1
- package/dist/src/pipeline/metadata-store.d.ts +2 -1
- package/dist/src/pipeline/metadata-store.d.ts.map +1 -1
- package/dist/src/pipeline/metadata-store.js +7 -1
- package/dist/src/pipeline/metadata-store.js.map +1 -1
- package/dist/src/pipeline/party-feed-provider.d.ts +2 -3
- package/dist/src/pipeline/party-feed-provider.d.ts.map +1 -1
- package/dist/src/pipeline/party-feed-provider.js +2 -17
- package/dist/src/pipeline/party-feed-provider.js.map +1 -1
- package/dist/src/pipeline/{party-core.d.ts → party-pipeline.d.ts} +23 -9
- package/dist/src/pipeline/party-pipeline.d.ts.map +1 -0
- package/dist/src/pipeline/{party-core.js → party-pipeline.js} +26 -25
- package/dist/src/pipeline/party-pipeline.js.map +1 -0
- package/dist/src/pipeline/party-pipeline.test.d.ts +2 -0
- package/dist/src/pipeline/party-pipeline.test.d.ts.map +1 -0
- package/dist/src/pipeline/{party-core.test.js → party-pipeline.test.js} +50 -33
- package/dist/src/pipeline/party-pipeline.test.js.map +1 -0
- package/dist/src/protocol/index.js +5 -1
- package/dist/src/protocol/index.js.map +1 -1
- package/dist/src/snapshots/index.js +5 -1
- package/dist/src/snapshots/index.js.map +1 -1
- package/dist/src/snapshots/snapshot-generator.d.ts +2 -2
- package/dist/src/snapshots/snapshot-generator.d.ts.map +1 -1
- package/dist/src/snapshots/snapshot-generator.js.map +1 -1
- package/dist/src/snapshots/snapshot-store.js +1 -1
- package/dist/src/snapshots/snapshot-store.js.map +1 -1
- package/dist/src/testing/index.js +5 -1
- package/dist/src/testing/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -18
- package/src/echo.test.ts +15 -4
- package/src/echo.ts +1 -0
- package/src/halo/halo-factory.ts +6 -6
- package/src/halo/halo-party.ts +10 -8
- package/src/halo/halo.ts +2 -2
- package/src/halo/party-opener.ts +3 -1
- package/src/invitations/greeting-initiator.ts +10 -9
- package/src/invitations/halo-recovery-initiator.ts +3 -3
- package/src/invitations/offline-invitation-claimer.ts +2 -2
- package/src/packlets/database/data-mirror.test.ts +2 -2
- package/src/packlets/database/database-backend.ts +7 -6
- package/src/packlets/database/item-demuxer.test.ts +20 -18
- package/src/packlets/database/item-demuxer.ts +5 -4
- package/src/packlets/database/testing.ts +3 -6
- package/src/packlets/database/timeframe-clock.ts +4 -0
- package/src/parties/data-party.test.ts +39 -8
- package/src/parties/data-party.ts +31 -9
- package/src/parties/party-factory.ts +19 -17
- package/src/parties/party-manager.test.ts +15 -9
- package/src/parties/party-manager.ts +9 -6
- package/src/pipeline/{pipeline.test.ts → feed-muxer.test.ts} +20 -16
- package/src/pipeline/{pipeline.ts → feed-muxer.ts} +39 -51
- package/src/pipeline/index.ts +2 -2
- package/src/pipeline/message-selector.ts +6 -5
- package/src/pipeline/metadata-store.ts +8 -2
- package/src/pipeline/party-feed-provider.ts +3 -16
- package/src/pipeline/{party-core.test.ts → party-pipeline.test.ts} +49 -27
- package/src/pipeline/{party-core.ts → party-pipeline.ts} +53 -24
- package/src/snapshots/snapshot-generator.ts +2 -2
- package/src/snapshots/snapshot-store.ts +1 -1
- package/dist/src/pipeline/party-core.d.ts.map +0 -1
- package/dist/src/pipeline/party-core.js.map +0 -1
- package/dist/src/pipeline/party-core.test.d.ts +0 -2
- package/dist/src/pipeline/party-core.test.d.ts.map +0 -1
- package/dist/src/pipeline/party-core.test.js.map +0 -1
- package/dist/src/pipeline/pipeline.d.ts.map +0 -1
- package/dist/src/pipeline/pipeline.js.map +0 -1
- package/dist/src/pipeline/pipeline.test.d.ts +0 -2
- package/dist/src/pipeline/pipeline.test.d.ts.map +0 -1
- package/dist/src/pipeline/pipeline.test.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-db",
|
|
3
|
-
"version": "2.33.5-dev.
|
|
3
|
+
"version": "2.33.5-dev.c6e30fbc",
|
|
4
4
|
"description": "ECHO database.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -10,22 +10,22 @@
|
|
|
10
10
|
"src"
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@dxos/async": "2.33.5-dev.
|
|
14
|
-
"@dxos/codec-protobuf": "2.33.5-dev.
|
|
15
|
-
"@dxos/credentials": "2.33.5-dev.
|
|
16
|
-
"@dxos/crypto": "2.33.5-dev.
|
|
17
|
-
"@dxos/debug": "2.33.5-dev.
|
|
18
|
-
"@dxos/echo-protocol": "2.33.5-dev.
|
|
19
|
-
"@dxos/feed-store": "2.33.5-dev.
|
|
20
|
-
"@dxos/mesh-protocol": "2.33.5-dev.
|
|
21
|
-
"@dxos/model-factory": "2.33.5-dev.
|
|
22
|
-
"@dxos/network-manager": "2.33.5-dev.
|
|
23
|
-
"@dxos/object-model": "2.33.5-dev.
|
|
24
|
-
"@dxos/protocol-plugin-presence": "2.33.5-dev.
|
|
25
|
-
"@dxos/protocol-plugin-replicator": "2.33.5-dev.
|
|
26
|
-
"@dxos/protocols": "2.33.5-dev.
|
|
27
|
-
"@dxos/random-access-multi-storage": "2.33.5-dev.
|
|
28
|
-
"@dxos/util": "2.33.5-dev.
|
|
13
|
+
"@dxos/async": "2.33.5-dev.c6e30fbc",
|
|
14
|
+
"@dxos/codec-protobuf": "2.33.5-dev.c6e30fbc",
|
|
15
|
+
"@dxos/credentials": "2.33.5-dev.c6e30fbc",
|
|
16
|
+
"@dxos/crypto": "2.33.5-dev.c6e30fbc",
|
|
17
|
+
"@dxos/debug": "2.33.5-dev.c6e30fbc",
|
|
18
|
+
"@dxos/echo-protocol": "2.33.5-dev.c6e30fbc",
|
|
19
|
+
"@dxos/feed-store": "2.33.5-dev.c6e30fbc",
|
|
20
|
+
"@dxos/mesh-protocol": "2.33.5-dev.c6e30fbc",
|
|
21
|
+
"@dxos/model-factory": "2.33.5-dev.c6e30fbc",
|
|
22
|
+
"@dxos/network-manager": "2.33.5-dev.c6e30fbc",
|
|
23
|
+
"@dxos/object-model": "2.33.5-dev.c6e30fbc",
|
|
24
|
+
"@dxos/protocol-plugin-presence": "2.33.5-dev.c6e30fbc",
|
|
25
|
+
"@dxos/protocol-plugin-replicator": "2.33.5-dev.c6e30fbc",
|
|
26
|
+
"@dxos/protocols": "2.33.5-dev.c6e30fbc",
|
|
27
|
+
"@dxos/random-access-multi-storage": "2.33.5-dev.c6e30fbc",
|
|
28
|
+
"@dxos/util": "2.33.5-dev.c6e30fbc",
|
|
29
29
|
"base-x": "~3.0.9",
|
|
30
30
|
"buffer-json-encoding": "^1.0.2",
|
|
31
31
|
"debug": "^4.3.3",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"faker": "^5.1.0",
|
|
57
57
|
"memdown": "^5.1.0",
|
|
58
58
|
"mocha": "~8.4.0",
|
|
59
|
-
"typescript": "^4.
|
|
59
|
+
"typescript": "^4.7.2"
|
|
60
60
|
},
|
|
61
61
|
"publishConfig": {
|
|
62
62
|
"access": "public"
|
package/src/echo.test.ts
CHANGED
|
@@ -707,10 +707,6 @@ describe('ECHO', () => {
|
|
|
707
707
|
expect(partyA.isOpen).toBe(true);
|
|
708
708
|
expect(partyA.isActive).toBe(true);
|
|
709
709
|
|
|
710
|
-
await partyA.database
|
|
711
|
-
.select({ type: 'example:item/test' })
|
|
712
|
-
.exec()
|
|
713
|
-
.update.waitFor(result => result.entities.length > 0);
|
|
714
710
|
expect(partyA.database.select({ type: 'example:item/test' }).exec().entities.length).toEqual(1);
|
|
715
711
|
}).timeout(10_000);
|
|
716
712
|
|
|
@@ -834,6 +830,21 @@ describe('ECHO', () => {
|
|
|
834
830
|
expect(members[1].displayName).toBe('B');
|
|
835
831
|
});
|
|
836
832
|
|
|
833
|
+
test('optimistic mutations on ObjectModel', async () => {
|
|
834
|
+
const echo = await setup({ createProfile: true, displayName: 'A' });
|
|
835
|
+
const party = await echo.createParty();
|
|
836
|
+
const item = await party.database.createItem({ model: ObjectModel });
|
|
837
|
+
|
|
838
|
+
const committed = item.model
|
|
839
|
+
.builder()
|
|
840
|
+
.set('key', 'value')
|
|
841
|
+
.commit();
|
|
842
|
+
expect(item.model.get('key')).toEqual('value');
|
|
843
|
+
|
|
844
|
+
await committed;
|
|
845
|
+
expect(item.model.get('key')).toEqual('value');
|
|
846
|
+
});
|
|
847
|
+
|
|
837
848
|
// TODO(burdon): Fix.
|
|
838
849
|
// Note: The reason I wrote this test is because it does not seem to be working properly in Teamwork.
|
|
839
850
|
// I don't seem to be receiving an update after which `party.title` holds correct value.
|
package/src/echo.ts
CHANGED
package/src/halo/halo-factory.ts
CHANGED
|
@@ -13,8 +13,7 @@ import {
|
|
|
13
13
|
Keyring,
|
|
14
14
|
KeyType,
|
|
15
15
|
Filter,
|
|
16
|
-
SecretProvider
|
|
17
|
-
KeyHint
|
|
16
|
+
SecretProvider
|
|
18
17
|
} from '@dxos/credentials';
|
|
19
18
|
import { keyToString, PublicKey, keyPairFromSeedPhrase } from '@dxos/crypto';
|
|
20
19
|
import { ModelFactory } from '@dxos/model-factory';
|
|
@@ -23,7 +22,7 @@ import { ObjectModel } from '@dxos/object-model';
|
|
|
23
22
|
|
|
24
23
|
import { createHaloPartyAdmissionMessage, GreetingInitiator, HaloRecoveryInitiator, InvitationDescriptor, InvitationDescriptorType, OfflineInvitationClaimer } from '../invitations';
|
|
25
24
|
import { PARTY_ITEM_TYPE } from '../parties';
|
|
26
|
-
import { PartyFeedProvider,
|
|
25
|
+
import { PartyFeedProvider, PipelineOptions } from '../pipeline';
|
|
27
26
|
import { CredentialsSigner } from '../protocol/credentials-signer';
|
|
28
27
|
import { SnapshotStore } from '../snapshots';
|
|
29
28
|
import {
|
|
@@ -51,19 +50,20 @@ export class HaloFactory {
|
|
|
51
50
|
private readonly _snapshotStore: SnapshotStore,
|
|
52
51
|
private readonly _feedProviderFactory: (partyKey: PublicKey) => PartyFeedProvider,
|
|
53
52
|
private readonly _keyring: Keyring,
|
|
54
|
-
private readonly _options:
|
|
53
|
+
private readonly _options: PipelineOptions = {}
|
|
55
54
|
) {}
|
|
56
55
|
|
|
57
|
-
async constructParty (
|
|
56
|
+
async constructParty (feedHints: PublicKey[]): Promise<HaloParty> {
|
|
58
57
|
const credentialsSigner = CredentialsSigner.createDirectDeviceSigner(this._keyring);
|
|
59
58
|
const feedProvider = this._feedProviderFactory(credentialsSigner.getIdentityKey().publicKey);
|
|
59
|
+
const writableFeed = await feedProvider.createOrOpenWritableFeed();
|
|
60
60
|
const halo = new HaloParty(
|
|
61
61
|
this._modelFactory,
|
|
62
62
|
this._snapshotStore,
|
|
63
63
|
feedProvider,
|
|
64
64
|
credentialsSigner,
|
|
65
65
|
this._networkManager,
|
|
66
|
-
|
|
66
|
+
[...feedHints, writableFeed.key],
|
|
67
67
|
undefined,
|
|
68
68
|
this._options
|
|
69
69
|
);
|
package/src/halo/halo-party.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { NetworkManager } from '@dxos/network-manager';
|
|
|
14
14
|
|
|
15
15
|
import { InvitationAuthenticator, InvitationDescriptor, InvitationFactory, InvitationOptions } from '../invitations';
|
|
16
16
|
import { PARTY_ITEM_TYPE } from '../parties';
|
|
17
|
-
import { PartyFeedProvider, PartyProtocolFactory,
|
|
17
|
+
import { PartyFeedProvider, PartyProtocolFactory, PartyPipeline, PipelineOptions } from '../pipeline';
|
|
18
18
|
import { createAuthenticator, createAuthPlugin, createCredentialsProvider, createHaloRecoveryPlugin } from '../protocol';
|
|
19
19
|
import { CredentialsSigner } from '../protocol/credentials-signer';
|
|
20
20
|
import { createReplicatorPlugin } from '../protocol/replicator-plugin';
|
|
@@ -41,7 +41,7 @@ export interface JoinedParty {
|
|
|
41
41
|
export class HaloParty {
|
|
42
42
|
public readonly update = new Event<void>();
|
|
43
43
|
|
|
44
|
-
private readonly _partyCore:
|
|
44
|
+
private readonly _partyCore: PartyPipeline;
|
|
45
45
|
private _invitationManager?: InvitationFactory;
|
|
46
46
|
private _protocol?: PartyProtocolFactory;
|
|
47
47
|
|
|
@@ -54,17 +54,16 @@ export class HaloParty {
|
|
|
54
54
|
private readonly _feedProvider: PartyFeedProvider,
|
|
55
55
|
private readonly _credentialsSigner: CredentialsSigner,
|
|
56
56
|
private readonly _networkManager: NetworkManager,
|
|
57
|
-
private readonly
|
|
58
|
-
_initialTimeframe: Timeframe | undefined,
|
|
59
|
-
_options:
|
|
57
|
+
private readonly _feedHints: PublicKey[] = [],
|
|
58
|
+
private readonly _initialTimeframe: Timeframe | undefined,
|
|
59
|
+
_options: PipelineOptions
|
|
60
60
|
) {
|
|
61
|
-
this._partyCore = new
|
|
61
|
+
this._partyCore = new PartyPipeline(
|
|
62
62
|
_credentialsSigner.getIdentityKey().publicKey,
|
|
63
63
|
_feedProvider,
|
|
64
64
|
modelFactory,
|
|
65
65
|
snapshotStore,
|
|
66
66
|
_credentialsSigner.getIdentityKey().publicKey,
|
|
67
|
-
_initialTimeframe,
|
|
68
67
|
_options
|
|
69
68
|
);
|
|
70
69
|
|
|
@@ -145,7 +144,10 @@ export class HaloParty {
|
|
|
145
144
|
return this;
|
|
146
145
|
}
|
|
147
146
|
|
|
148
|
-
await this._partyCore.open(
|
|
147
|
+
await this._partyCore.open({
|
|
148
|
+
feedHints: this._feedHints,
|
|
149
|
+
initialTimeframe: this._initialTimeframe
|
|
150
|
+
});
|
|
149
151
|
|
|
150
152
|
this._invitationManager = new InvitationFactory(
|
|
151
153
|
this._partyCore.processor,
|
package/src/halo/halo.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { NetworkManager } from '@dxos/network-manager';
|
|
|
15
15
|
import { ResultSet } from '../api';
|
|
16
16
|
import { InvitationAuthenticator, InvitationDescriptor, InvitationOptions } from '../invitations';
|
|
17
17
|
import { OpenProgress } from '../parties';
|
|
18
|
-
import { MetadataStore,
|
|
18
|
+
import { MetadataStore, PipelineOptions, PartyFeedProvider } from '../pipeline';
|
|
19
19
|
import { SnapshotStore } from '../snapshots';
|
|
20
20
|
import { Contact } from './contact-manager';
|
|
21
21
|
import { HaloFactory } from './halo-factory';
|
|
@@ -36,7 +36,7 @@ export interface HaloConfiguration {
|
|
|
36
36
|
modelFactory: ModelFactory,
|
|
37
37
|
snapshotStore: SnapshotStore,
|
|
38
38
|
feedProviderFactory: (partyKey: PublicKey) => PartyFeedProvider,
|
|
39
|
-
options:
|
|
39
|
+
options: PipelineOptions
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
package/src/halo/party-opener.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import debug from 'debug';
|
|
6
6
|
|
|
7
|
+
import { KeyType } from '@dxos/credentials';
|
|
7
8
|
import { SubscriptionGroup, Unsubscribe } from '@dxos/util';
|
|
8
9
|
|
|
9
10
|
import { PartyManager } from '../parties';
|
|
@@ -25,7 +26,8 @@ export const autoPartyOpener = (preferences: Preferences, partyManager: PartyMan
|
|
|
25
26
|
for (const partyDesc of values) {
|
|
26
27
|
if (!partyManager.parties.some(x => x.key === partyDesc.partyKey)) {
|
|
27
28
|
log(`Auto-opening new Party from HALO: ${partyDesc.partyKey.toHex()} hints=${JSON.stringify(partyDesc.keyHints)}`);
|
|
28
|
-
|
|
29
|
+
const feedHints = partyDesc.keyHints.filter(hint => hint.type === KeyType.FEED).map(hint => hint.publicKey!);
|
|
30
|
+
await partyManager.addParty(partyDesc.partyKey, feedHints);
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
}));
|
|
@@ -7,6 +7,7 @@ import debug from 'debug';
|
|
|
7
7
|
|
|
8
8
|
import { waitForEvent } from '@dxos/async';
|
|
9
9
|
import {
|
|
10
|
+
ERR_GREET_CONNECTED_TO_SWARM_TIMEOUT,
|
|
10
11
|
createEnvelopeMessage,
|
|
11
12
|
createGreetingBeginMessage,
|
|
12
13
|
createGreetingFinishMessage,
|
|
@@ -19,8 +20,8 @@ import {
|
|
|
19
20
|
Message,
|
|
20
21
|
SecretProvider,
|
|
21
22
|
WithTypeUrl,
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
SignedMessage,
|
|
24
|
+
NotarizeResponse
|
|
24
25
|
} from '@dxos/credentials';
|
|
25
26
|
import { keyToString, PublicKey } from '@dxos/crypto';
|
|
26
27
|
import { FullyConnectedTopology, NetworkManager } from '@dxos/network-manager';
|
|
@@ -46,10 +47,9 @@ export class GreetingInitiator {
|
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
49
|
* @param _networkManager
|
|
49
|
-
* @param _identity
|
|
50
50
|
* @param _invitationDescriptor
|
|
51
|
-
* @param
|
|
52
|
-
*
|
|
51
|
+
* @param _getMessagesToNotarize
|
|
52
|
+
* Returns a list of credential messages that the inviter will be asked to write into the control feed.
|
|
53
53
|
*/
|
|
54
54
|
constructor (
|
|
55
55
|
private readonly _networkManager: NetworkManager,
|
|
@@ -93,7 +93,8 @@ export class GreetingInitiator {
|
|
|
93
93
|
log(keyToString(localPeerId), 'connecting to', keyToString(swarmKey));
|
|
94
94
|
|
|
95
95
|
const peerJoinedWaiter = waitForEvent(this._greeterPlugin, 'peer:joined',
|
|
96
|
-
(remotePeerId: any) => remotePeerId && Buffer.from(responderPeerId).equals(remotePeerId),
|
|
96
|
+
(remotePeerId: any) => remotePeerId && Buffer.from(responderPeerId).equals(remotePeerId),
|
|
97
|
+
timeout, ERR_GREET_CONNECTED_TO_SWARM_TIMEOUT);
|
|
97
98
|
|
|
98
99
|
await this._networkManager.joinProtocolSwarm({
|
|
99
100
|
topic: PublicKey.from(swarmKey),
|
|
@@ -111,7 +112,7 @@ export class GreetingInitiator {
|
|
|
111
112
|
/**
|
|
112
113
|
* Called after connecting to initiate greeting protocol exchange.
|
|
113
114
|
*/
|
|
114
|
-
async redeemInvitation (secretProvider: SecretProvider) {
|
|
115
|
+
async redeemInvitation (secretProvider: SecretProvider): Promise<{ partyKey: PublicKey, hints: PublicKey[] }> {
|
|
115
116
|
assert(this._state === GreetingState.CONNECTED);
|
|
116
117
|
const { swarmKey } = this._invitationDescriptor;
|
|
117
118
|
|
|
@@ -150,7 +151,7 @@ export class GreetingInitiator {
|
|
|
150
151
|
const credentialMessages = await this._getMessagesToNotarize(PublicKey.from(partyKey), nonce);
|
|
151
152
|
|
|
152
153
|
// Send the signed payload to the greeting responder.
|
|
153
|
-
const notarizeResponse = await this._greeterPlugin.send(responderPeerId,
|
|
154
|
+
const notarizeResponse: NotarizeResponse = await this._greeterPlugin.send(responderPeerId,
|
|
154
155
|
createGreetingNotarizeMessage(secret, credentialMessages as WithTypeUrl<Message>[]));
|
|
155
156
|
|
|
156
157
|
//
|
|
@@ -171,7 +172,7 @@ export class GreetingInitiator {
|
|
|
171
172
|
this._state = GreetingState.SUCCEEDED;
|
|
172
173
|
return {
|
|
173
174
|
partyKey,
|
|
174
|
-
hints: notarizeResponse.
|
|
175
|
+
hints: notarizeResponse.feedHints ?? []
|
|
175
176
|
};
|
|
176
177
|
}
|
|
177
178
|
|
|
@@ -23,7 +23,7 @@ import { keyToBuffer, keyToString, PublicKey, randomBytes, verify } from '@dxos/
|
|
|
23
23
|
import { FullyConnectedTopology, NetworkManager } from '@dxos/network-manager';
|
|
24
24
|
|
|
25
25
|
import { InvalidInvitationError } from '../packlets/errors';
|
|
26
|
-
import { CredentialsSigner } from '../protocol
|
|
26
|
+
import { CredentialsSigner } from '../protocol';
|
|
27
27
|
import { greetingProtocolProvider } from './greeting-protocol-provider';
|
|
28
28
|
import { GreetingState } from './greeting-responder';
|
|
29
29
|
import { InvitationDescriptor, InvitationDescriptorType } from './invitation-descriptor';
|
|
@@ -72,8 +72,8 @@ export class HaloRecoveryInitiator {
|
|
|
72
72
|
this._greeterPlugin = new GreetingCommandPlugin(this._peerId, async () => false);
|
|
73
73
|
|
|
74
74
|
log('Connecting');
|
|
75
|
-
const peerJoinedWaiter = waitForEvent(
|
|
76
|
-
() => this._greeterPlugin?.peers.length, timeout);
|
|
75
|
+
const peerJoinedWaiter = waitForEvent(
|
|
76
|
+
this._greeterPlugin, 'peer:joined', () => !!this._greeterPlugin?.peers.length, timeout);
|
|
77
77
|
|
|
78
78
|
await this._networkManager.joinProtocolSwarm({
|
|
79
79
|
topic: PublicKey.from(swarmKey),
|
|
@@ -70,8 +70,8 @@ export class OfflineInvitationClaimer {
|
|
|
70
70
|
this._greeterPlugin = new GreetingCommandPlugin(localPeerId, async () => false);
|
|
71
71
|
|
|
72
72
|
log('Connecting');
|
|
73
|
-
const peerJoinedWaiter = waitForEvent(
|
|
74
|
-
() => this._greeterPlugin?.peers.length, timeout);
|
|
73
|
+
const peerJoinedWaiter = waitForEvent(
|
|
74
|
+
this._greeterPlugin, 'peer:joined', () => !!this._greeterPlugin?.peers.length, timeout);
|
|
75
75
|
|
|
76
76
|
await this._networkManager.joinProtocolSwarm({
|
|
77
77
|
topic: PublicKey.from(swarmKey),
|
|
@@ -26,8 +26,8 @@ describe('DataMirror', () => {
|
|
|
26
26
|
const itemManager = new ItemManager(modelFactory, PublicKey.random(), feed);
|
|
27
27
|
const itemDemuxer = new ItemDemuxer(itemManager, modelFactory, { snapshots: true });
|
|
28
28
|
|
|
29
|
-
const
|
|
30
|
-
feed.written.on(([msg, meta]) =>
|
|
29
|
+
const process = itemDemuxer.open();
|
|
30
|
+
feed.written.on(([msg, meta]) => process({
|
|
31
31
|
data: msg,
|
|
32
32
|
meta: { ...meta, memberKey: PublicKey.random(), timeframe: new Timeframe() }
|
|
33
33
|
} as any));
|
|
@@ -10,7 +10,7 @@ import { ModelFactory } from '@dxos/model-factory';
|
|
|
10
10
|
|
|
11
11
|
import { DataMirror } from './data-mirror';
|
|
12
12
|
import { DataServiceHost } from './data-service-host';
|
|
13
|
-
import { ItemDemuxer, ItemDemuxerOptions } from './item-demuxer';
|
|
13
|
+
import { EchoProcessor, ItemDemuxer, ItemDemuxerOptions } from './item-demuxer';
|
|
14
14
|
import { ItemManager } from './item-manager';
|
|
15
15
|
|
|
16
16
|
const log = debug('dxos:echo-db:database-backend');
|
|
@@ -42,12 +42,11 @@ export interface DatabaseBackend {
|
|
|
42
42
|
* Write operations result in mutations being written to the outgoing stream.
|
|
43
43
|
*/
|
|
44
44
|
export class FeedDatabaseBackend implements DatabaseBackend {
|
|
45
|
-
private
|
|
45
|
+
private _echoProcessor!: EchoProcessor;
|
|
46
46
|
private _itemManager!: ItemManager;
|
|
47
47
|
private _itemDemuxer!: ItemDemuxer;
|
|
48
48
|
|
|
49
49
|
constructor (
|
|
50
|
-
private readonly _inboundStream: NodeJS.ReadableStream,
|
|
51
50
|
private readonly _outboundStream: FeedWriter<EchoEnvelope> | undefined,
|
|
52
51
|
private readonly _snapshot?: DatabaseSnapshot,
|
|
53
52
|
private readonly _options: ItemDemuxerOptions = {}
|
|
@@ -56,16 +55,18 @@ export class FeedDatabaseBackend implements DatabaseBackend {
|
|
|
56
55
|
async open (itemManager: ItemManager, modelFactory: ModelFactory) {
|
|
57
56
|
this._itemManager = itemManager;
|
|
58
57
|
this._itemDemuxer = new ItemDemuxer(itemManager, modelFactory, this._options);
|
|
59
|
-
this.
|
|
60
|
-
this._inboundStream.pipe(this._itemDemuxerInboundStream);
|
|
58
|
+
this._echoProcessor = this._itemDemuxer.open();
|
|
61
59
|
|
|
62
60
|
if (this._snapshot) {
|
|
63
61
|
await this._itemDemuxer.restoreFromSnapshot(this._snapshot);
|
|
64
62
|
}
|
|
65
63
|
}
|
|
66
64
|
|
|
65
|
+
get echoProcessor () {
|
|
66
|
+
return this._echoProcessor;
|
|
67
|
+
}
|
|
68
|
+
|
|
67
69
|
async close () {
|
|
68
|
-
this._inboundStream?.unpipe(this._itemDemuxerInboundStream);
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
get isReadOnly (): boolean {
|
|
@@ -9,8 +9,7 @@ import { it as test } from 'mocha';
|
|
|
9
9
|
import { latch } from '@dxos/async';
|
|
10
10
|
import { createId, PublicKey } from '@dxos/crypto';
|
|
11
11
|
import { checkType } from '@dxos/debug';
|
|
12
|
-
import {
|
|
13
|
-
import { createTransform } from '@dxos/feed-store';
|
|
12
|
+
import { EchoEnvelope, MockFeedWriter, Timeframe } from '@dxos/echo-protocol';
|
|
14
13
|
import { ModelFactory, TestModel } from '@dxos/model-factory';
|
|
15
14
|
import { ObjectModel } from '@dxos/object-model';
|
|
16
15
|
|
|
@@ -32,7 +31,7 @@ describe('Item demuxer', () => {
|
|
|
32
31
|
const itemDemuxer = new ItemDemuxer(itemManager, modelFactory);
|
|
33
32
|
|
|
34
33
|
const inboundStream = itemDemuxer.open();
|
|
35
|
-
feedWriter.written.on(([msg, meta]) => inboundStream
|
|
34
|
+
feedWriter.written.on(([msg, meta]) => inboundStream({
|
|
36
35
|
data: msg,
|
|
37
36
|
meta: { ...meta, memberKey }
|
|
38
37
|
} as any));
|
|
@@ -96,28 +95,31 @@ describe('Item demuxer', () => {
|
|
|
96
95
|
const modelFactory = new ModelFactory()
|
|
97
96
|
.registerModel(ObjectModel);
|
|
98
97
|
|
|
99
|
-
const
|
|
100
|
-
async (message
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
timeframe: new Timeframe()
|
|
106
|
-
},
|
|
107
|
-
data: message
|
|
108
|
-
})
|
|
109
|
-
);
|
|
110
|
-
const itemManager = new ItemManager(modelFactory, PublicKey.random(), createMockFeedWriterFromStream(writeStream));
|
|
98
|
+
const itemManager = new ItemManager(modelFactory, PublicKey.random(), {
|
|
99
|
+
write: async (message) => {
|
|
100
|
+
void processEchoMessage(message);
|
|
101
|
+
return { feedKey: PublicKey.random(), seq: 0 };
|
|
102
|
+
}
|
|
103
|
+
});
|
|
111
104
|
const itemDemuxer = new ItemDemuxer(itemManager, modelFactory);
|
|
112
|
-
|
|
105
|
+
const processor = itemDemuxer.open();
|
|
106
|
+
const processEchoMessage = (message: EchoEnvelope) => processor({
|
|
107
|
+
meta: {
|
|
108
|
+
feedKey: PublicKey.random(),
|
|
109
|
+
memberKey: PublicKey.random(),
|
|
110
|
+
seq: 0,
|
|
111
|
+
timeframe: new Timeframe()
|
|
112
|
+
},
|
|
113
|
+
data: message
|
|
114
|
+
});
|
|
113
115
|
|
|
114
|
-
|
|
116
|
+
void processEchoMessage(checkType<EchoEnvelope>({
|
|
115
117
|
itemId: 'foo',
|
|
116
118
|
genesis: {
|
|
117
119
|
modelType: TestModel.meta.type
|
|
118
120
|
}
|
|
119
121
|
}));
|
|
120
|
-
|
|
122
|
+
void processEchoMessage(checkType<EchoEnvelope>({
|
|
121
123
|
itemId: 'bar',
|
|
122
124
|
genesis: {
|
|
123
125
|
modelType: ObjectModel.meta.type
|
|
@@ -8,7 +8,6 @@ import debug from 'debug';
|
|
|
8
8
|
import { Event } from '@dxos/async';
|
|
9
9
|
import { failUndefined } from '@dxos/debug';
|
|
10
10
|
import { DatabaseSnapshot, IEchoStream, ItemID, ItemSnapshot, LinkSnapshot } from '@dxos/echo-protocol';
|
|
11
|
-
import { createWritable } from '@dxos/feed-store';
|
|
12
11
|
import { Model, ModelFactory, ModelMessage } from '@dxos/model-factory';
|
|
13
12
|
|
|
14
13
|
import { Entity } from './entity';
|
|
@@ -22,6 +21,8 @@ export interface ItemDemuxerOptions {
|
|
|
22
21
|
snapshots?: boolean
|
|
23
22
|
}
|
|
24
23
|
|
|
24
|
+
export type EchoProcessor = (message: IEchoStream) => Promise<void>
|
|
25
|
+
|
|
25
26
|
/**
|
|
26
27
|
* Creates a stream that consumes `IEchoStream` messages and routes them to the associated items.
|
|
27
28
|
* @param itemManager
|
|
@@ -35,7 +36,7 @@ export class ItemDemuxer {
|
|
|
35
36
|
private readonly _options: ItemDemuxerOptions = {}
|
|
36
37
|
) {}
|
|
37
38
|
|
|
38
|
-
open ():
|
|
39
|
+
open (): EchoProcessor {
|
|
39
40
|
this._modelFactory.registered.on(async model => {
|
|
40
41
|
for (const item of this._itemManager.getUninitializedEntities()) {
|
|
41
42
|
if (item._stateManager.modelType === model.meta.type) {
|
|
@@ -46,7 +47,7 @@ export class ItemDemuxer {
|
|
|
46
47
|
|
|
47
48
|
// TODO(burdon): Factor out.
|
|
48
49
|
// TODO(burdon): Should this implement some "back-pressure" (hints) to the PartyProcessor?
|
|
49
|
-
return
|
|
50
|
+
return async (message: IEchoStream) => {
|
|
50
51
|
const { data: { itemId, genesis, itemMutation, mutation, snapshot }, meta } = message;
|
|
51
52
|
assert(itemId);
|
|
52
53
|
|
|
@@ -111,7 +112,7 @@ export class ItemDemuxer {
|
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
this.mutation.emit(message);
|
|
114
|
-
}
|
|
115
|
+
};
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
createSnapshot (): DatabaseSnapshot {
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
// Copyright 2021 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Readable } from 'stream';
|
|
6
|
-
|
|
7
5
|
import { PublicKey } from '@dxos/crypto';
|
|
8
6
|
import { EchoEnvelope, MockFeedWriter, Timeframe } from '@dxos/echo-protocol';
|
|
9
7
|
import { ModelFactory } from '@dxos/model-factory';
|
|
@@ -15,12 +13,11 @@ import { FeedDatabaseBackend, RemoteDatabaseBackend } from './database-backend';
|
|
|
15
13
|
|
|
16
14
|
export const createInMemoryDatabase = async (modelFactory: ModelFactory) => {
|
|
17
15
|
const feed = new MockFeedWriter<EchoEnvelope>();
|
|
18
|
-
const
|
|
19
|
-
feed.written.on(([data, meta]) =>
|
|
20
|
-
|
|
16
|
+
const backend = new FeedDatabaseBackend(feed, undefined, { snapshots: true });
|
|
17
|
+
feed.written.on(([data, meta]) => backend.echoProcessor({ data, meta: { ...meta, memberKey: PublicKey.random(), timeframe: new Timeframe([[meta.feedKey, meta.seq]]) } }));
|
|
21
18
|
const database = new Database(
|
|
22
19
|
modelFactory,
|
|
23
|
-
|
|
20
|
+
backend,
|
|
24
21
|
PublicKey.random()
|
|
25
22
|
);
|
|
26
23
|
|
|
@@ -28,4 +28,8 @@ export class TimeframeClock {
|
|
|
28
28
|
const gaps = Timeframe.dependencies(timeframe, this._timeframe);
|
|
29
29
|
return !gaps.isEmpty();
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
async waitUntilReached (target: Timeframe) {
|
|
33
|
+
await this.update.waitForCondition(() => Timeframe.dependencies(target, this._timeframe).isEmpty());
|
|
34
|
+
}
|
|
31
35
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import expect from 'expect';
|
|
6
6
|
import { it as test } from 'mocha';
|
|
7
7
|
|
|
8
|
-
import { createKeyAdmitMessage, createPartyGenesisMessage, defaultSecretProvider,
|
|
8
|
+
import { createKeyAdmitMessage, createPartyGenesisMessage, defaultSecretProvider, Keyring, KeyType, codec as haloCodec } from '@dxos/credentials';
|
|
9
9
|
import { PublicKey } from '@dxos/crypto';
|
|
10
10
|
import { codec } from '@dxos/echo-protocol';
|
|
11
11
|
import { FeedStore } from '@dxos/feed-store';
|
|
@@ -22,7 +22,7 @@ import { SnapshotStore } from '../snapshots';
|
|
|
22
22
|
import { DataParty } from './data-party';
|
|
23
23
|
|
|
24
24
|
describe('DataParty', () => {
|
|
25
|
-
const createParty = async (identity: IdentityCredentials, partyKey: PublicKey,
|
|
25
|
+
const createParty = async (identity: IdentityCredentials, partyKey: PublicKey, feedHints: PublicKey[]) => {
|
|
26
26
|
|
|
27
27
|
const storage = createStorage('', StorageType.RAM);
|
|
28
28
|
const snapshotStore = new SnapshotStore(storage.directory('snapshots'));
|
|
@@ -31,17 +31,20 @@ describe('DataParty', () => {
|
|
|
31
31
|
const modelFactory = new ModelFactory().registerModel(ObjectModel);
|
|
32
32
|
const networkManager = new NetworkManager();
|
|
33
33
|
const partyFeedProvider = new PartyFeedProvider(metadataStore, identity.keyring, feedStore, partyKey);
|
|
34
|
+
const writableFeed = await partyFeedProvider.createOrOpenWritableFeed();
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
const party = new DataParty(
|
|
36
37
|
partyKey,
|
|
37
38
|
modelFactory,
|
|
38
39
|
snapshotStore,
|
|
39
40
|
partyFeedProvider,
|
|
41
|
+
metadataStore,
|
|
40
42
|
identity.createCredentialsSigner(),
|
|
41
43
|
identity.preferences,
|
|
42
|
-
networkManager
|
|
43
|
-
hints
|
|
44
|
+
networkManager
|
|
44
45
|
);
|
|
46
|
+
party._setFeedHints([...feedHints, writableFeed.key]);
|
|
47
|
+
return party;
|
|
45
48
|
};
|
|
46
49
|
|
|
47
50
|
test('open & close', async () => {
|
|
@@ -74,6 +77,33 @@ describe('DataParty', () => {
|
|
|
74
77
|
await party.close();
|
|
75
78
|
});
|
|
76
79
|
|
|
80
|
+
test('data is immediately available after re-opening', async () => {
|
|
81
|
+
const keyring = new Keyring();
|
|
82
|
+
const identity = await createTestIdentityCredentials(keyring);
|
|
83
|
+
const partyKey = await keyring.createKeyRecord({ type: KeyType.PARTY });
|
|
84
|
+
const party = await createParty(identity, partyKey.publicKey, []);
|
|
85
|
+
await party.open();
|
|
86
|
+
|
|
87
|
+
const feed = await party.getWriteFeed();
|
|
88
|
+
await party.credentialsWriter.write(createPartyGenesisMessage(
|
|
89
|
+
keyring,
|
|
90
|
+
partyKey,
|
|
91
|
+
feed.key,
|
|
92
|
+
partyKey
|
|
93
|
+
));
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < 10; i++) {
|
|
96
|
+
await party.database.createItem({ type: 'test:item' });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await party.close();
|
|
100
|
+
await party.open();
|
|
101
|
+
|
|
102
|
+
expect(party.database.select({ type: 'test:item' }).exec().entities).toHaveLength(10);
|
|
103
|
+
|
|
104
|
+
await party.close();
|
|
105
|
+
});
|
|
106
|
+
|
|
77
107
|
test('authenticates its own credentials', async () => {
|
|
78
108
|
const keyring = new Keyring();
|
|
79
109
|
const identity = await createTestIdentityCredentials(keyring);
|
|
@@ -88,6 +118,7 @@ describe('DataParty', () => {
|
|
|
88
118
|
feed.key,
|
|
89
119
|
partyKey
|
|
90
120
|
));
|
|
121
|
+
await party.processor.feedAdded.waitForCount(1);
|
|
91
122
|
|
|
92
123
|
const authenticator = createAuthenticator(party.processor, identity.createCredentialsSigner(), party.credentialsWriter);
|
|
93
124
|
const credentialsProvider = createCredentialsProvider(identity.createCredentialsSigner(), party.key, feed.key);
|
|
@@ -112,6 +143,8 @@ describe('DataParty', () => {
|
|
|
112
143
|
feed.key,
|
|
113
144
|
partyKey
|
|
114
145
|
));
|
|
146
|
+
await party.processor.feedAdded.waitForCount(1);
|
|
147
|
+
|
|
115
148
|
const authenticator = createAuthenticator(party.processor, identityA.createCredentialsSigner(), party.credentialsWriter);
|
|
116
149
|
|
|
117
150
|
const identityB = await deriveTestDeviceCredentials(identityA);
|
|
@@ -145,9 +178,7 @@ describe('DataParty', () => {
|
|
|
145
178
|
));
|
|
146
179
|
|
|
147
180
|
const identityB = await deriveTestDeviceCredentials(identityA);
|
|
148
|
-
const partyB = await createParty(identityB, partyKey.publicKey, [
|
|
149
|
-
{ type: KeyType.FEED, publicKey: feedA.key }
|
|
150
|
-
]);
|
|
181
|
+
const partyB = await createParty(identityB, partyKey.publicKey, [feedA.key]);
|
|
151
182
|
await partyB.open();
|
|
152
183
|
|
|
153
184
|
await partyA.database.createItem({ type: 'test:item-a' });
|