@dxos/echo-db 2.33.5-dev.b9f5bea6 → 2.33.5-dev.cb045a79
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 +0 -4
- package/dist/src/echo.test.js.map +1 -1
- package/dist/src/halo/halo-factory.d.ts +2 -2
- package/dist/src/halo/halo-factory.d.ts.map +1 -1
- package/dist/src/halo/halo-factory.js +2 -1
- package/dist/src/halo/halo-factory.js.map +1 -1
- package/dist/src/halo/halo-party.d.ts +2 -2
- package/dist/src/halo/halo-party.d.ts.map +1 -1
- package/dist/src/halo/halo-party.js +1 -1
- 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/index.js +5 -1
- package/dist/src/index.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/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 +5 -4
- package/dist/src/parties/data-party.d.ts.map +1 -1
- package/dist/src/parties/data-party.js +22 -5
- package/dist/src/parties/data-party.js.map +1 -1
- package/dist/src/parties/data-party.test.js +20 -1
- 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 +4 -3
- package/dist/src/parties/party-factory.d.ts.map +1 -1
- package/dist/src/parties/party-factory.js +13 -9
- package/dist/src/parties/party-factory.js.map +1 -1
- package/dist/src/parties/party-manager.d.ts.map +1 -1
- package/dist/src/parties/party-manager.js +6 -3
- package/dist/src/parties/party-manager.js.map +1 -1
- package/dist/src/parties/party-manager.test.js +2 -2
- 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} +33 -40
- 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} +16 -13
- 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/metadata-store.d.ts +2 -1
- package/dist/src/pipeline/metadata-store.d.ts.map +1 -1
- package/dist/src/pipeline/metadata-store.js +6 -0
- 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} +6 -6
- package/dist/src/pipeline/party-pipeline.d.ts.map +1 -0
- package/dist/src/pipeline/{party-core.js → party-pipeline.js} +21 -17
- 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} +33 -16
- 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/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 +0 -4
- package/src/echo.ts +1 -0
- package/src/halo/halo-factory.ts +4 -3
- package/src/halo/halo-party.ts +4 -4
- package/src/halo/halo.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 +33 -3
- package/src/parties/data-party.ts +27 -6
- package/src/parties/party-factory.ts +17 -14
- package/src/parties/party-manager.test.ts +3 -1
- package/src/parties/party-manager.ts +7 -2
- package/src/pipeline/{pipeline.test.ts → feed-muxer.test.ts} +19 -14
- package/src/pipeline/{pipeline.ts → feed-muxer.ts} +38 -51
- package/src/pipeline/index.ts +2 -2
- package/src/pipeline/metadata-store.ts +7 -1
- package/src/pipeline/party-feed-provider.ts +3 -16
- package/src/pipeline/{party-core.test.ts → party-pipeline.test.ts} +31 -9
- package/src/pipeline/{party-core.ts → party-pipeline.ts} +27 -14
- package/src/snapshots/snapshot-generator.ts +2 -2
- 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.cb045a79",
|
|
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.cb045a79",
|
|
14
|
+
"@dxos/codec-protobuf": "2.33.5-dev.cb045a79",
|
|
15
|
+
"@dxos/credentials": "2.33.5-dev.cb045a79",
|
|
16
|
+
"@dxos/crypto": "2.33.5-dev.cb045a79",
|
|
17
|
+
"@dxos/debug": "2.33.5-dev.cb045a79",
|
|
18
|
+
"@dxos/echo-protocol": "2.33.5-dev.cb045a79",
|
|
19
|
+
"@dxos/feed-store": "2.33.5-dev.cb045a79",
|
|
20
|
+
"@dxos/mesh-protocol": "2.33.5-dev.cb045a79",
|
|
21
|
+
"@dxos/model-factory": "2.33.5-dev.cb045a79",
|
|
22
|
+
"@dxos/network-manager": "2.33.5-dev.cb045a79",
|
|
23
|
+
"@dxos/object-model": "2.33.5-dev.cb045a79",
|
|
24
|
+
"@dxos/protocol-plugin-presence": "2.33.5-dev.cb045a79",
|
|
25
|
+
"@dxos/protocol-plugin-replicator": "2.33.5-dev.cb045a79",
|
|
26
|
+
"@dxos/protocols": "2.33.5-dev.cb045a79",
|
|
27
|
+
"@dxos/random-access-multi-storage": "2.33.5-dev.cb045a79",
|
|
28
|
+
"@dxos/util": "2.33.5-dev.cb045a79",
|
|
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
|
|
package/src/echo.ts
CHANGED
package/src/halo/halo-factory.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { ObjectModel } from '@dxos/object-model';
|
|
|
22
22
|
|
|
23
23
|
import { createHaloPartyAdmissionMessage, GreetingInitiator, HaloRecoveryInitiator, InvitationDescriptor, InvitationDescriptorType, OfflineInvitationClaimer } from '../invitations';
|
|
24
24
|
import { PARTY_ITEM_TYPE } from '../parties';
|
|
25
|
-
import { PartyFeedProvider,
|
|
25
|
+
import { PartyFeedProvider, PipelineOptions } from '../pipeline';
|
|
26
26
|
import { CredentialsSigner } from '../protocol/credentials-signer';
|
|
27
27
|
import { SnapshotStore } from '../snapshots';
|
|
28
28
|
import {
|
|
@@ -50,19 +50,20 @@ export class HaloFactory {
|
|
|
50
50
|
private readonly _snapshotStore: SnapshotStore,
|
|
51
51
|
private readonly _feedProviderFactory: (partyKey: PublicKey) => PartyFeedProvider,
|
|
52
52
|
private readonly _keyring: Keyring,
|
|
53
|
-
private readonly _options:
|
|
53
|
+
private readonly _options: PipelineOptions = {}
|
|
54
54
|
) {}
|
|
55
55
|
|
|
56
56
|
async constructParty (feedHints: PublicKey[]): Promise<HaloParty> {
|
|
57
57
|
const credentialsSigner = CredentialsSigner.createDirectDeviceSigner(this._keyring);
|
|
58
58
|
const feedProvider = this._feedProviderFactory(credentialsSigner.getIdentityKey().publicKey);
|
|
59
|
+
const writableFeed = await feedProvider.createOrOpenWritableFeed();
|
|
59
60
|
const halo = new HaloParty(
|
|
60
61
|
this._modelFactory,
|
|
61
62
|
this._snapshotStore,
|
|
62
63
|
feedProvider,
|
|
63
64
|
credentialsSigner,
|
|
64
65
|
this._networkManager,
|
|
65
|
-
feedHints,
|
|
66
|
+
[...feedHints, writableFeed.key],
|
|
66
67
|
undefined,
|
|
67
68
|
this._options
|
|
68
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
|
|
|
@@ -56,9 +56,9 @@ export class HaloParty {
|
|
|
56
56
|
private readonly _networkManager: NetworkManager,
|
|
57
57
|
private readonly _feedHints: PublicKey[] = [],
|
|
58
58
|
private readonly _initialTimeframe: Timeframe | undefined,
|
|
59
|
-
_options:
|
|
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,
|
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
|
/**
|
|
@@ -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
|
}
|
|
@@ -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
|
-
feedHints
|
|
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);
|
|
@@ -17,7 +17,7 @@ import { ResultSet } from '../api';
|
|
|
17
17
|
import { ActivationOptions, PartyPreferences, Preferences } from '../halo';
|
|
18
18
|
import { InvitationFactory } from '../invitations';
|
|
19
19
|
import { Database, Item } from '../packlets/database';
|
|
20
|
-
import { PartyFeedProvider, PartyProtocolFactory,
|
|
20
|
+
import { PartyFeedProvider, PartyProtocolFactory, PartyPipeline, PipelineOptions, MetadataStore } from '../pipeline';
|
|
21
21
|
import { createAuthPlugin, createOfflineInvitationPlugin, createAuthenticator, createCredentialsProvider } from '../protocol';
|
|
22
22
|
import { CredentialsSigner } from '../protocol/credentials-signer';
|
|
23
23
|
import { createReplicatorPlugin } from '../protocol/replicator-plugin';
|
|
@@ -42,25 +42,26 @@ export interface PartyMember {
|
|
|
42
42
|
export class DataParty {
|
|
43
43
|
public readonly update = new Event<void>();
|
|
44
44
|
|
|
45
|
-
private readonly _partyCore:
|
|
45
|
+
private readonly _partyCore: PartyPipeline;
|
|
46
46
|
private readonly _preferences?: PartyPreferences;
|
|
47
47
|
private _invitationManager?: InvitationFactory;
|
|
48
48
|
private _protocol?: PartyProtocolFactory;
|
|
49
|
+
private _feedHints: PublicKey[] = []
|
|
49
50
|
|
|
50
51
|
constructor (
|
|
51
52
|
partyKey: PartyKey,
|
|
52
53
|
modelFactory: ModelFactory,
|
|
53
54
|
snapshotStore: SnapshotStore,
|
|
54
55
|
private readonly _feedProvider: PartyFeedProvider,
|
|
56
|
+
private readonly _metadataStore: MetadataStore,
|
|
55
57
|
private readonly _credentialsSigner: CredentialsSigner,
|
|
56
58
|
// TODO(dmaretskyi): Pull this out to a higher level. Should preferences be part of client API instead?
|
|
57
59
|
private readonly _profilePreferences: Preferences | undefined,
|
|
58
60
|
private readonly _networkManager: NetworkManager,
|
|
59
|
-
private readonly _feedHints: PublicKey[] = [],
|
|
60
61
|
private readonly _initialTimeframe?: Timeframe,
|
|
61
|
-
_options:
|
|
62
|
+
_options: PipelineOptions = {}
|
|
62
63
|
) {
|
|
63
|
-
this._partyCore = new
|
|
64
|
+
this._partyCore = new PartyPipeline(
|
|
64
65
|
partyKey,
|
|
65
66
|
_feedProvider,
|
|
66
67
|
modelFactory,
|
|
@@ -140,6 +141,13 @@ export class DataParty {
|
|
|
140
141
|
await this._preferences?.setLastKnownTitle(title);
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
/**
|
|
145
|
+
* @internal
|
|
146
|
+
*/
|
|
147
|
+
_setFeedHints (feedHints: PublicKey[]) {
|
|
148
|
+
this._feedHints = feedHints;
|
|
149
|
+
}
|
|
150
|
+
|
|
143
151
|
/**
|
|
144
152
|
* Opens the pipeline and connects the streams.
|
|
145
153
|
*/
|
|
@@ -150,9 +158,19 @@ export class DataParty {
|
|
|
150
158
|
return this;
|
|
151
159
|
}
|
|
152
160
|
|
|
161
|
+
// TODO(dmaretskyi): May be undefined in some tests.
|
|
162
|
+
const party = this._metadataStore.getParty(this._partyCore.key);
|
|
163
|
+
|
|
153
164
|
await this._partyCore.open({
|
|
154
165
|
feedHints: this._feedHints,
|
|
155
|
-
initialTimeframe: this._initialTimeframe
|
|
166
|
+
initialTimeframe: this._initialTimeframe,
|
|
167
|
+
targetTimeframe: party?.latestTimeframe
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Keep updating latest reached timeframe in the metadata.
|
|
171
|
+
// This timeframe will be waited for when opening the party next time.
|
|
172
|
+
this._partyCore.timeframeUpdate.on(timeframe => {
|
|
173
|
+
void this._metadataStore.setTimeframe(this._partyCore.key, timeframe);
|
|
156
174
|
});
|
|
157
175
|
|
|
158
176
|
this._invitationManager = new InvitationFactory(
|
|
@@ -198,6 +216,9 @@ export class DataParty {
|
|
|
198
216
|
return this;
|
|
199
217
|
}
|
|
200
218
|
|
|
219
|
+
// Save the latest reached timeframe.
|
|
220
|
+
await this._metadataStore.setTimeframe(this._partyCore.key, this._partyCore.timeframe);
|
|
221
|
+
|
|
201
222
|
await this._partyCore.close();
|
|
202
223
|
await this._protocol?.stop();
|
|
203
224
|
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
GreetingInitiator, InvitationDescriptor, InvitationDescriptorType, OfflineInvitationClaimer
|
|
26
26
|
} from '../invitations';
|
|
27
27
|
import { IdentityNotInitializedError } from '../packlets/errors';
|
|
28
|
-
import { PartyFeedProvider,
|
|
28
|
+
import { MetadataStore, PartyFeedProvider, PipelineOptions } from '../pipeline';
|
|
29
29
|
import { IdentityCredentialsProvider } from '../protocol/identity-credentials';
|
|
30
30
|
import { SnapshotStore } from '../snapshots';
|
|
31
31
|
import { DataParty, PARTY_ITEM_TYPE } from './data-party';
|
|
@@ -42,7 +42,8 @@ export class PartyFactory {
|
|
|
42
42
|
private readonly _modelFactory: ModelFactory,
|
|
43
43
|
private readonly _snapshotStore: SnapshotStore,
|
|
44
44
|
private readonly _feedProviderFactory: (partyKey: PublicKey) => PartyFeedProvider,
|
|
45
|
-
private readonly
|
|
45
|
+
private readonly _metadataStore: MetadataStore,
|
|
46
|
+
private readonly _options: PipelineOptions = {}
|
|
46
47
|
) {}
|
|
47
48
|
|
|
48
49
|
/**
|
|
@@ -56,11 +57,13 @@ export class PartyFactory {
|
|
|
56
57
|
const partyKey = await identity.keyring.createKeyRecord({ type: KeyType.PARTY });
|
|
57
58
|
const party = await this.constructParty(partyKey.publicKey);
|
|
58
59
|
|
|
60
|
+
const writableFeed = await party.getWriteFeed();
|
|
61
|
+
// Hint at the newly created writable feed so that we can start replicating from it.
|
|
62
|
+
party._setFeedHints([writableFeed.key]);
|
|
63
|
+
|
|
59
64
|
// Connect the pipeline.
|
|
60
65
|
await party.open();
|
|
61
66
|
|
|
62
|
-
const writableFeed = await party.getWriteFeed();
|
|
63
|
-
|
|
64
67
|
// PartyGenesis (self-signed by Party).
|
|
65
68
|
await party.credentialsWriter.write(createPartyGenesisMessage(
|
|
66
69
|
identity.keyring,
|
|
@@ -110,12 +113,9 @@ export class PartyFactory {
|
|
|
110
113
|
* @param partyKey
|
|
111
114
|
* @param hints
|
|
112
115
|
*/
|
|
113
|
-
async constructParty (partyKey: PartyKey,
|
|
116
|
+
async constructParty (partyKey: PartyKey, initialTimeframe?: Timeframe) {
|
|
114
117
|
const identity = this._identityProvider() ?? raise(new IdentityNotInitializedError());
|
|
115
118
|
|
|
116
|
-
// TODO(marik-d): Support read-only parties if this feed doesn't exist?
|
|
117
|
-
const feedProvider = this._feedProviderFactory(partyKey);
|
|
118
|
-
|
|
119
119
|
//
|
|
120
120
|
// Create the party.
|
|
121
121
|
//
|
|
@@ -123,11 +123,11 @@ export class PartyFactory {
|
|
|
123
123
|
partyKey,
|
|
124
124
|
this._modelFactory,
|
|
125
125
|
this._snapshotStore,
|
|
126
|
-
|
|
126
|
+
this._feedProviderFactory(partyKey),
|
|
127
|
+
this._metadataStore,
|
|
127
128
|
identity.createCredentialsSigner(),
|
|
128
129
|
identity.preferences,
|
|
129
130
|
this._networkManager,
|
|
130
|
-
feedHints,
|
|
131
131
|
initialTimeframe,
|
|
132
132
|
this._options
|
|
133
133
|
);
|
|
@@ -139,7 +139,7 @@ export class PartyFactory {
|
|
|
139
139
|
assert(snapshot.partyKey);
|
|
140
140
|
log(`Constructing ${humanize(snapshot.partyKey)} from snapshot at ${JSON.stringify(snapshot.timeframe)}.`);
|
|
141
141
|
|
|
142
|
-
const party = await this.constructParty(PublicKey.from(snapshot.partyKey),
|
|
142
|
+
const party = await this.constructParty(PublicKey.from(snapshot.partyKey), snapshot.timeframe);
|
|
143
143
|
await party.restoreFromSnapshot(snapshot);
|
|
144
144
|
return party;
|
|
145
145
|
}
|
|
@@ -172,7 +172,8 @@ export class PartyFactory {
|
|
|
172
172
|
|
|
173
173
|
await initiator.connect();
|
|
174
174
|
const { partyKey, hints } = await initiator.redeemInvitation(secretProvider);
|
|
175
|
-
const party = await this.constructParty(partyKey
|
|
175
|
+
const party = await this.constructParty(partyKey);
|
|
176
|
+
party._setFeedHints(hints);
|
|
176
177
|
await party.open();
|
|
177
178
|
await initiator.destroy();
|
|
178
179
|
|
|
@@ -197,11 +198,13 @@ export class PartyFactory {
|
|
|
197
198
|
const partyKey = await identity.keyring.createKeyRecord({ type: KeyType.PARTY });
|
|
198
199
|
const party = await this.constructParty(partyKey.publicKey);
|
|
199
200
|
|
|
201
|
+
const writableFeed = await party.getWriteFeed();
|
|
202
|
+
// Hint at the newly created writable feed so that we can start replicating from it.
|
|
203
|
+
party._setFeedHints([writableFeed.key]);
|
|
204
|
+
|
|
200
205
|
// Connect the pipeline.
|
|
201
206
|
await party.open();
|
|
202
207
|
|
|
203
|
-
const writableFeed = await party.getWriteFeed();
|
|
204
|
-
|
|
205
208
|
// PartyGenesis (self-signed by Party).
|
|
206
209
|
await party.credentialsWriter.write(createPartyGenesisMessage(
|
|
207
210
|
identity.keyring,
|
|
@@ -71,6 +71,7 @@ const setup = async () => {
|
|
|
71
71
|
modelFactory,
|
|
72
72
|
snapshotStore,
|
|
73
73
|
feedProviderFactory,
|
|
74
|
+
metadataStore,
|
|
74
75
|
{
|
|
75
76
|
writeLogger: messageLogger('<<<'),
|
|
76
77
|
readLogger: messageLogger('>>>')
|
|
@@ -161,7 +162,8 @@ describe('Party manager', () => {
|
|
|
161
162
|
networkManager,
|
|
162
163
|
modelFactory,
|
|
163
164
|
snapshotStore,
|
|
164
|
-
feedProviderFactory
|
|
165
|
+
feedProviderFactory,
|
|
166
|
+
metadataStore
|
|
165
167
|
);
|
|
166
168
|
const partyManager = new PartyManager(metadataStore, snapshotStore, () => identity, partyFactory);
|
|
167
169
|
|
|
@@ -9,7 +9,7 @@ import unionWith from 'lodash.unionwith';
|
|
|
9
9
|
import { Event, synchronized } from '@dxos/async';
|
|
10
10
|
import { KeyHint, KeyType, SecretProvider } from '@dxos/credentials';
|
|
11
11
|
import { PublicKey } from '@dxos/crypto';
|
|
12
|
-
import { timed } from '@dxos/debug';
|
|
12
|
+
import { failUndefined, timed } from '@dxos/debug';
|
|
13
13
|
import { PartyKey, PartySnapshot } from '@dxos/echo-protocol';
|
|
14
14
|
import { ComplexMap, boolGuard } from '@dxos/util';
|
|
15
15
|
|
|
@@ -94,9 +94,13 @@ export class PartyManager {
|
|
|
94
94
|
const partyKey = partyKeys[i];
|
|
95
95
|
if (!this._parties.has(partyKey)) {
|
|
96
96
|
const snapshot = await this._snapshotStore.load(partyKey);
|
|
97
|
+
|
|
98
|
+
const metadata = this._metadataStore.getParty(partyKey) ?? failUndefined();
|
|
99
|
+
|
|
97
100
|
const party = snapshot
|
|
98
101
|
? await this._partyFactory.constructPartyFromSnapshot(snapshot)
|
|
99
102
|
: await this._partyFactory.constructParty(partyKey);
|
|
103
|
+
party._setFeedHints(metadata.feedKeys ?? []);
|
|
100
104
|
|
|
101
105
|
const isActive = identity?.preferences?.isPartyActive(partyKey) ?? true;
|
|
102
106
|
if (isActive) {
|
|
@@ -173,7 +177,8 @@ export class PartyManager {
|
|
|
173
177
|
}
|
|
174
178
|
|
|
175
179
|
log(`Adding party partyKey=${partyKey.toHex()} hints=${feedHints.length}`);
|
|
176
|
-
const party = await this._partyFactory.constructParty(partyKey
|
|
180
|
+
const party = await this._partyFactory.constructParty(partyKey);
|
|
181
|
+
party._setFeedHints(feedHints);
|
|
177
182
|
await party.open();
|
|
178
183
|
await this._metadataStore.addParty(party.key);
|
|
179
184
|
this._setParty(party);
|