@dxos/echo-pipeline 0.1.41 → 0.1.45-next.45667d4

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.
Files changed (74) hide show
  1. package/dist/lib/browser/{chunk-4YAT2XJW.mjs → chunk-2NGLSBL4.mjs} +1128 -1017
  2. package/dist/lib/browser/chunk-2NGLSBL4.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1 -3
  4. package/dist/lib/browser/index.mjs.map +1 -1
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +72 -48
  7. package/dist/lib/browser/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/index.cjs +1048 -938
  9. package/dist/lib/node/index.cjs.map +4 -4
  10. package/dist/lib/node/meta.json +1 -1
  11. package/dist/lib/node/testing/index.cjs +735 -540
  12. package/dist/lib/node/testing/index.cjs.map +4 -4
  13. package/dist/types/src/dbhost/database-host.d.ts +2 -4
  14. package/dist/types/src/dbhost/database-host.d.ts.map +1 -1
  15. package/dist/types/src/dbhost/snapshot-store.d.ts +2 -0
  16. package/dist/types/src/dbhost/snapshot-store.d.ts.map +1 -1
  17. package/dist/types/src/metadata/metadata-store.d.ts +3 -2
  18. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  19. package/dist/types/src/pipeline/pipeline.d.ts +27 -11
  20. package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
  21. package/dist/types/src/pipeline/timeframe-clock.d.ts +1 -0
  22. package/dist/types/src/pipeline/timeframe-clock.d.ts.map +1 -1
  23. package/dist/types/src/space/auth.d.ts +4 -4
  24. package/dist/types/src/space/auth.d.ts.map +1 -1
  25. package/dist/types/src/space/control-pipeline.d.ts +2 -4
  26. package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
  27. package/dist/types/src/space/data-pipeline.d.ts +19 -10
  28. package/dist/types/src/space/data-pipeline.d.ts.map +1 -1
  29. package/dist/types/src/space/index.d.ts +0 -1
  30. package/dist/types/src/space/index.d.ts.map +1 -1
  31. package/dist/types/src/space/space-manager.d.ts +14 -14
  32. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  33. package/dist/types/src/space/space-protocol.d.ts +2 -2
  34. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  35. package/dist/types/src/space/space.d.ts +20 -7
  36. package/dist/types/src/space/space.d.ts.map +1 -1
  37. package/dist/types/src/testing/database-test-rig.d.ts.map +1 -1
  38. package/dist/types/src/testing/test-agent-builder.d.ts +15 -5
  39. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  40. package/package.json +30 -30
  41. package/src/common/feeds.ts +2 -2
  42. package/src/dbhost/data-service-host.ts +10 -10
  43. package/src/dbhost/data-service.ts +1 -1
  44. package/src/dbhost/database-host.ts +1 -9
  45. package/src/dbhost/snapshot-store.test.ts +2 -2
  46. package/src/dbhost/snapshot-store.ts +16 -1
  47. package/src/metadata/metadata-store.ts +14 -16
  48. package/src/pipeline/message-selector.ts +1 -1
  49. package/src/pipeline/pipeline.test.ts +68 -44
  50. package/src/pipeline/pipeline.ts +118 -35
  51. package/src/pipeline/timeframe-clock.ts +8 -2
  52. package/src/space/auth.ts +6 -6
  53. package/src/space/control-pipeline.test.ts +19 -20
  54. package/src/space/control-pipeline.ts +3 -5
  55. package/src/space/data-pipeline.ts +131 -69
  56. package/src/space/index.ts +0 -1
  57. package/src/space/replication.browser.test.ts +8 -8
  58. package/src/space/space-manager.browser.test.ts +3 -3
  59. package/src/space/space-manager.ts +25 -20
  60. package/src/space/space-protocol.browser.test.ts +1 -1
  61. package/src/space/space-protocol.test.ts +6 -6
  62. package/src/space/space-protocol.ts +7 -7
  63. package/src/space/space.test.ts +137 -48
  64. package/src/space/space.ts +82 -40
  65. package/src/testing/database-test-rig.ts +25 -24
  66. package/src/testing/test-agent-builder.ts +88 -49
  67. package/src/testing/test-feed-builder.ts +1 -1
  68. package/src/testing/util.ts +15 -15
  69. package/src/tests/database-unit.test.ts +4 -4
  70. package/src/tests/database.test.ts +1 -1
  71. package/dist/lib/browser/chunk-4YAT2XJW.mjs.map +0 -7
  72. package/dist/types/src/space/genesis.d.ts +0 -5
  73. package/dist/types/src/space/genesis.d.ts.map +0 -1
  74. package/src/space/genesis.ts +0 -69
@@ -4,10 +4,11 @@
4
4
 
5
5
  import expect from 'expect';
6
6
  import assert from 'node:assert';
7
+ import { promisify } from 'node:util';
7
8
 
8
- import { CredentialGenerator } from '@dxos/credentials';
9
- import { AdmittedFeed } from '@dxos/protocols/proto/dxos/halo/credentials';
10
- import { describe, test, afterTest } from '@dxos/test';
9
+ import { createCredential, CredentialGenerator } from '@dxos/credentials';
10
+ import { log } from '@dxos/log';
11
+ import { afterTest, describe, test } from '@dxos/test';
11
12
 
12
13
  import { TestAgentBuilder, testLocalDatabase } from '../testing';
13
14
 
@@ -19,32 +20,21 @@ describe('space/space', () => {
19
20
  const builder = new TestAgentBuilder();
20
21
  afterTest(async () => await builder.close());
21
22
  const agent = await builder.createPeer();
22
- const [space, db] = await agent.createSpace();
23
+ const space = await agent.createSpace();
23
24
 
24
25
  await space.open();
26
+
25
27
  expect(space.isOpen).toBeTruthy();
26
28
  afterTest(() => space.close());
27
29
 
28
- {
29
- // Genesis
30
- const generator = new CredentialGenerator(agent.keyring, agent.identityKey, agent.deviceKey);
31
- const credentials = [
32
- ...(await generator.createSpaceGenesis(space.key, space.controlFeedKey!)),
33
- await generator.createFeedAdmission(space.key, space.dataFeedKey!, AdmittedFeed.Designation.DATA)
34
- ];
30
+ await agent.spaceGenesis(space);
35
31
 
36
- for (const credential of credentials) {
37
- await space.controlPipeline.writer.write({
38
- credential: { credential }
39
- });
40
- }
41
-
42
- // TODO(burdon): Debugging only.
43
- await space.controlPipeline.state!.waitUntilTimeframe(space.controlPipeline.state!.endTimeframe);
44
- }
32
+ await space.controlPipeline.state!.waitUntilTimeframe(space.controlPipeline.state!.endTimeframe);
33
+ await space.initializeDataPipeline();
34
+ await space.dataPipeline.ensureEpochInitialized();
45
35
 
46
- assert(db.databaseBackend);
47
- await testLocalDatabase(db);
36
+ assert(space.dataPipeline.databaseHost);
37
+ await testLocalDatabase(space.dataPipeline);
48
38
 
49
39
  await builder.close();
50
40
  expect(space.isOpen).toBeFalsy();
@@ -57,47 +47,36 @@ describe('space/space', () => {
57
47
  //
58
48
  // Agent 1
59
49
  //
60
- const [agent1, space1, db1] = await run(async () => {
50
+ const [agent1, space1] = await run(async () => {
61
51
  const agent = await builder.createPeer();
62
- const [space, db] = await agent.createSpace(agent.identityKey);
52
+ const space = await agent.createSpace(agent.identityKey);
63
53
 
64
54
  await space.open();
65
55
  expect(space.isOpen).toBeTruthy();
66
56
  afterTest(() => space.close());
67
57
 
68
- {
69
- // Genesis
70
- const generator = new CredentialGenerator(agent.keyring, agent.identityKey, agent.deviceKey);
71
- const credentials = [
72
- ...(await generator.createSpaceGenesis(space.key, space.controlFeedKey!)),
73
- await generator.createFeedAdmission(space.key, space.dataFeedKey!, AdmittedFeed.Designation.DATA)
74
- ];
75
-
76
- for (const credential of credentials) {
77
- await space.controlPipeline.writer.write({
78
- credential: { credential }
79
- });
80
- }
81
-
82
- await space.controlPipeline.state!.waitUntilTimeframe(space.controlPipeline.state!.endTimeframe);
83
- }
58
+ await agent.spaceGenesis(space);
59
+
60
+ await space.controlPipeline.state!.waitUntilTimeframe(space.controlPipeline.state!.endTimeframe);
61
+ await space.initializeDataPipeline();
62
+ await space.dataPipeline.ensureEpochInitialized();
84
63
 
85
- return [agent, space, db];
64
+ return [agent, space];
86
65
  });
87
66
 
88
67
  //
89
68
  // Agent 2
90
69
  //
91
- const [agent2, space2, db2] = await run(async () => {
70
+ const [agent2, space2] = await run(async () => {
92
71
  // NOTE: The genesisKey would be passed as part of the invitation.
93
72
  const agent = await builder.createPeer();
94
- const [space, db] = await agent.createSpace(agent.identityKey, space1.key, space1.genesisFeedKey);
73
+ const space = await agent.createSpace(agent.identityKey, space1.key, space1.genesisFeedKey);
95
74
 
96
75
  await space.open();
97
76
  expect(space.isOpen).toBeTruthy();
98
77
  afterTest(() => space.close());
99
78
 
100
- return [agent, space, db];
79
+ return [agent, space];
101
80
  });
102
81
 
103
82
  expect(agent1).toBeDefined();
@@ -112,16 +91,19 @@ describe('space/space', () => {
112
91
  agent2.deviceKey,
113
92
  space2.controlFeedKey!,
114
93
  space2.dataFeedKey!,
115
- space1.genesisFeedKey
94
+ space1.genesisFeedKey,
116
95
  );
117
96
 
118
97
  for (const credential of credentials) {
119
98
  await space1.controlPipeline.writer.write({
120
- credential: { credential }
99
+ credential: { credential },
121
100
  });
122
101
  }
123
102
  }
124
103
 
104
+ await space2.initializeDataPipeline();
105
+ await space2.dataPipeline.ensureEpochInitialized();
106
+
125
107
  {
126
108
  // Initial data exchange.
127
109
 
@@ -134,11 +116,118 @@ describe('space/space', () => {
134
116
 
135
117
  // TODO(burdon): Write multiple items (extract for all tests).
136
118
 
137
- await testLocalDatabase(db1, db2);
138
- await testLocalDatabase(db2, db1);
119
+ await testLocalDatabase(space1.dataPipeline, space2.dataPipeline);
120
+ await testLocalDatabase(space2.dataPipeline, space1.dataPipeline);
139
121
 
140
122
  await builder.close();
141
123
  expect(space1.isOpen).toBeFalsy();
142
124
  expect(space2.isOpen).toBeFalsy();
143
125
  });
126
+
127
+ test('open & close', async () => {
128
+ const builder = new TestAgentBuilder();
129
+ afterTest(async () => await builder.close());
130
+ const agent = await builder.createPeer();
131
+ const space1 = await agent.createSpace();
132
+
133
+ await space1.open();
134
+
135
+ expect(space1.isOpen).toBeTruthy();
136
+ afterTest(() => space1.close());
137
+
138
+ await agent.spaceGenesis(space1);
139
+
140
+ await space1.controlPipeline.state!.waitUntilTimeframe(space1.controlPipeline.state!.endTimeframe);
141
+ await space1.initializeDataPipeline();
142
+ await space1.dataPipeline.ensureEpochInitialized();
143
+
144
+ assert(space1.dataPipeline.databaseHost);
145
+ await testLocalDatabase(space1.dataPipeline);
146
+
147
+ const objectCount = space1.dataPipeline.itemManager.entities.size;
148
+
149
+ await space1.close();
150
+ expect(space1.isOpen).toBeFalsy();
151
+
152
+ // Re-open.
153
+ const space2 = await agent.createSpace(agent.identityKey, space1.key, space1.genesisFeedKey, space1.dataFeedKey);
154
+
155
+ await space2.open();
156
+
157
+ await space2.controlPipeline.state!.waitUntilTimeframe(space2.controlPipeline.state!.endTimeframe);
158
+ await space2.initializeDataPipeline();
159
+ await space2.dataPipeline.ensureEpochInitialized();
160
+
161
+ space2.dataPipeline.setTargetTimeframe(space2.dataPipeline.pipelineState!.endTimeframe);
162
+ await space2.dataPipeline.pipelineState!.waitUntilReachedTargetTimeframe();
163
+
164
+ assert(space2.dataPipeline.databaseHost);
165
+ expect(space2.dataPipeline.itemManager.entities.size).toEqual(objectCount);
166
+
167
+ await testLocalDatabase(space2.dataPipeline);
168
+ });
169
+
170
+ test('create epoch', async () => {
171
+ const builder = new TestAgentBuilder();
172
+ afterTest(async () => await builder.close());
173
+ const agent = await builder.createPeer();
174
+ const space1 = await agent.createSpace();
175
+
176
+ await space1.open();
177
+
178
+ expect(space1.isOpen).toBeTruthy();
179
+ afterTest(() => space1.close());
180
+
181
+ await agent.spaceGenesis(space1);
182
+
183
+ await space1.controlPipeline.state!.waitUntilTimeframe(space1.controlPipeline.state!.endTimeframe);
184
+ await space1.initializeDataPipeline();
185
+ await space1.dataPipeline.ensureEpochInitialized();
186
+
187
+ assert(space1.dataPipeline.databaseHost);
188
+ await testLocalDatabase(space1.dataPipeline);
189
+
190
+ const objectCount = space1.dataPipeline.itemManager.entities.size;
191
+
192
+ const epoch = await space1.dataPipeline.createEpoch();
193
+ await space1.controlPipeline.writer.write({
194
+ credential: {
195
+ credential: await createCredential({
196
+ issuer: agent.identityKey,
197
+ subject: space1.key,
198
+ assertion: {
199
+ '@type': 'dxos.halo.credentials.Epoch',
200
+ ...epoch,
201
+ },
202
+ signer: agent.keyring,
203
+ }),
204
+ },
205
+ });
206
+
207
+ await space1.close();
208
+ expect(space1.isOpen).toBeFalsy();
209
+
210
+ // Clear the data feed - epoch snapshot should have the data.
211
+ const feed = await agent.feedStore.openFeed(space1.dataFeedKey!);
212
+ await promisify(feed.core.clear.bind(feed.core))(0, feed.length);
213
+
214
+ log.break();
215
+
216
+ // Re-open.
217
+ const space2 = await agent.createSpace(agent.identityKey, space1.key, space1.genesisFeedKey, space1.dataFeedKey);
218
+
219
+ await space2.open();
220
+
221
+ await space2.controlPipeline.state!.waitUntilTimeframe(space2.controlPipeline.state!.endTimeframe);
222
+ await space2.initializeDataPipeline();
223
+ await space2.dataPipeline.ensureEpochInitialized();
224
+
225
+ space2.dataPipeline.setTargetTimeframe(space2.dataPipeline.pipelineState!.endTimeframe);
226
+ await space2.dataPipeline.pipelineState!.waitUntilReachedTargetTimeframe();
227
+
228
+ assert(space2.dataPipeline.databaseHost);
229
+ expect(space2.dataPipeline.itemManager.entities.size).toEqual(objectCount);
230
+
231
+ await testLocalDatabase(space2.dataPipeline);
232
+ });
144
233
  });
@@ -5,17 +5,21 @@
5
5
  import assert from 'node:assert';
6
6
 
7
7
  import { Event, synchronized, trackLeaks, Lock } from '@dxos/async';
8
- import { FeedInfo } from '@dxos/credentials';
8
+ import { CredentialConsumer, FeedInfo } from '@dxos/credentials';
9
9
  import { FeedWrapper } from '@dxos/feed-store';
10
10
  import { PublicKey } from '@dxos/keys';
11
11
  import { log, logInfo } from '@dxos/log';
12
+ import { ModelFactory } from '@dxos/model-factory';
12
13
  import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
13
14
  import { AdmittedFeed, Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
14
15
  import { Timeframe } from '@dxos/timeframe';
15
16
  import { AsyncCallback, Callback } from '@dxos/util';
16
17
 
17
- import { Pipeline, PipelineAccessor } from '../pipeline';
18
+ import { SnapshotManager } from '../dbhost';
19
+ import { MetadataStore } from '../metadata';
20
+ import { PipelineAccessor } from '../pipeline';
18
21
  import { ControlPipeline } from './control-pipeline';
22
+ import { DataPipeline } from './data-pipeline';
19
23
  import { SpaceProtocol } from './space-protocol';
20
24
 
21
25
  // TODO(burdon): Factor out?
@@ -26,6 +30,13 @@ export type SpaceParams = {
26
30
  protocol: SpaceProtocol;
27
31
  genesisFeed: FeedWrapper<FeedMessage>;
28
32
  feedProvider: FeedProvider;
33
+ modelFactory: ModelFactory;
34
+ metadataStore: MetadataStore;
35
+ snapshotManager: SnapshotManager;
36
+ memberKey: PublicKey;
37
+
38
+ // TODO(dmaretskyi): Superseded by epochs.
39
+ snapshotId?: string | undefined;
29
40
  };
30
41
 
31
42
  export type CreatePipelineParams = {
@@ -49,36 +60,44 @@ export class Space {
49
60
  private readonly _genesisFeedKey: PublicKey;
50
61
  private readonly _feedProvider: FeedProvider;
51
62
  private readonly _controlPipeline: ControlPipeline;
63
+ private readonly _dataPipeline: DataPipeline;
64
+ private readonly _snapshotManager: SnapshotManager;
65
+
66
+ // Processes epoch credentials.
67
+ private _dataPipelineCredentialConsumer?: CredentialConsumer<any> = undefined;
52
68
 
53
69
  private _isOpen = false;
54
70
  private _controlFeed?: FeedWrapper<FeedMessage>;
55
71
  private _dataFeed?: FeedWrapper<FeedMessage>;
56
- private _dataPipeline?: Pipeline;
57
72
 
58
- constructor({ spaceKey, protocol, genesisFeed, feedProvider }: SpaceParams) {
59
- assert(spaceKey && feedProvider);
60
- this._key = spaceKey;
61
- this._genesisFeedKey = genesisFeed.key;
62
- this._feedProvider = feedProvider;
63
-
64
- // TODO(dmaretskyi): Maybe reuse createPipeline method.
65
- this._controlPipeline = new ControlPipeline({ spaceKey, genesisFeed, feedProvider });
73
+ constructor(params: SpaceParams) {
74
+ assert(params.spaceKey && params.feedProvider);
75
+ this._key = params.spaceKey;
76
+ this._genesisFeedKey = params.genesisFeed.key;
77
+ this._feedProvider = params.feedProvider;
78
+ this._snapshotManager = params.snapshotManager;
79
+
80
+ this._controlPipeline = new ControlPipeline({
81
+ spaceKey: params.spaceKey,
82
+ genesisFeed: params.genesisFeed,
83
+ feedProvider: params.feedProvider,
84
+ });
66
85
 
67
86
  // TODO(dmaretskyi): Feed set abstraction.
68
87
  this._controlPipeline.onFeedAdmitted.set(async (info) => {
69
88
  if (info.assertion.designation === AdmittedFeed.Designation.DATA) {
70
89
  // We will add all existing data feeds when the data pipeline is initialized.
71
90
  await this._addFeedLock.executeSynchronized(async () => {
72
- if (this._dataPipeline) {
73
- if (!this._dataPipeline.hasFeed(info.key)) {
74
- return this._dataPipeline.addFeed(await this._feedProvider(info.key));
91
+ if (this._dataPipeline.pipeline) {
92
+ if (!this._dataPipeline.pipeline.hasFeed(info.key)) {
93
+ return this._dataPipeline.pipeline.addFeed(await this._feedProvider(info.key));
75
94
  }
76
95
  }
77
96
  });
78
97
  }
79
98
 
80
- if (!info.key.equals(genesisFeed.key)) {
81
- this.protocol.addFeed(await feedProvider(info.key));
99
+ if (!info.key.equals(params.genesisFeed.key)) {
100
+ this.protocol.addFeed(await params.feedProvider(info.key));
82
101
  }
83
102
  });
84
103
 
@@ -89,8 +108,32 @@ export class Space {
89
108
  });
90
109
 
91
110
  // Start replicating the genesis feed.
92
- this.protocol = protocol;
93
- this.protocol.addFeed(genesisFeed);
111
+ this.protocol = params.protocol;
112
+ this.protocol.addFeed(params.genesisFeed);
113
+
114
+ this._dataPipeline = new DataPipeline({
115
+ modelFactory: params.modelFactory,
116
+ metadataStore: params.metadataStore,
117
+ snapshotManager: params.snapshotManager,
118
+ memberKey: params.memberKey,
119
+ spaceKey: this._key,
120
+ feedInfoProvider: (feedKey) => this._controlPipeline.spaceState.feeds.get(feedKey),
121
+ snapshotId: params.snapshotId,
122
+ onPipelineCreated: async (pipeline) => {
123
+ if (this._dataFeed) {
124
+ pipeline.setWriteFeed(this._dataFeed);
125
+ }
126
+
127
+ // Add existing feeds.
128
+ await this._addFeedLock.executeSynchronized(async () => {
129
+ for (const feed of this._controlPipeline.spaceState.feeds.values()) {
130
+ if (feed.assertion.designation === AdmittedFeed.Designation.DATA && !pipeline.hasFeed(feed.key)) {
131
+ await pipeline.addFeed(await this._feedProvider(feed.key));
132
+ }
133
+ }
134
+ });
135
+ },
136
+ });
94
137
  }
95
138
 
96
139
  @logInfo
@@ -125,6 +168,14 @@ export class Space {
125
168
  return this._controlPipeline.pipeline;
126
169
  }
127
170
 
171
+ get dataPipeline(): DataPipeline {
172
+ return this._dataPipeline;
173
+ }
174
+
175
+ get snapshotManager(): SnapshotManager {
176
+ return this._snapshotManager;
177
+ }
178
+
128
179
  setControlFeed(feed: FeedWrapper<FeedMessage>) {
129
180
  assert(!this._controlFeed, 'Control feed already set.');
130
181
  this._controlFeed = feed;
@@ -135,7 +186,7 @@ export class Space {
135
186
  setDataFeed(feed: FeedWrapper<FeedMessage>) {
136
187
  assert(!this._dataFeed, 'Data feed already set.');
137
188
  this._dataFeed = feed;
138
- this._dataPipeline?.setWriteFeed(feed);
189
+ this._dataPipeline.pipeline?.setWriteFeed(feed);
139
190
  return this;
140
191
  }
141
192
 
@@ -163,6 +214,10 @@ export class Space {
163
214
  await this._controlPipeline.start();
164
215
  await this.protocol.start();
165
216
 
217
+ this._dataPipelineCredentialConsumer = this._controlPipeline.spaceState.registerProcessor(
218
+ this._dataPipeline.createCredentialProcessor(),
219
+ );
220
+
166
221
  this._isOpen = true;
167
222
  log('opened');
168
223
  }
@@ -173,6 +228,10 @@ export class Space {
173
228
  if (!this._isOpen) {
174
229
  return;
175
230
  }
231
+ await this._dataPipelineCredentialConsumer?.close();
232
+ this._dataPipelineCredentialConsumer = undefined;
233
+
234
+ await this._dataPipeline.close();
176
235
 
177
236
  // Closes in reverse order to open.
178
237
  await this.protocol.stop();
@@ -182,27 +241,10 @@ export class Space {
182
241
  log('closed');
183
242
  }
184
243
 
185
- // TODO(dmaretskyi): Make reusable.
186
- async createDataPipeline({ start }: CreatePipelineParams) {
244
+ async initializeDataPipeline() {
245
+ log.info('initializeDataPipeline');
187
246
  assert(this._isOpen, 'Space must be open to initialize data pipeline.');
188
- assert(!this._dataPipeline, 'Data pipeline already initialized.');
189
-
190
- const pipeline = new Pipeline(start);
191
- if (this._dataFeed) {
192
- pipeline.setWriteFeed(this._dataFeed);
193
- }
194
-
195
- this._dataPipeline = pipeline;
196
-
197
- // Add existing feeds.
198
- await this._addFeedLock.executeSynchronized(async () => {
199
- for (const feed of this._controlPipeline.spaceState.feeds.values()) {
200
- if (feed.assertion.designation === AdmittedFeed.Designation.DATA && !pipeline.hasFeed(feed.key)) {
201
- await pipeline.addFeed(await this._feedProvider(feed.key));
202
- }
203
- }
204
- });
205
-
206
- return pipeline;
247
+ await this._dataPipeline.open();
248
+ await this._dataPipelineCredentialConsumer!.open();
207
249
  }
208
250
  }
@@ -64,27 +64,28 @@ export class DatabaseTestPeer {
64
64
 
65
65
  async open() {
66
66
  this.hostItems = new ItemManager(this.modelFactory);
67
- this.host = new DatabaseHost(
68
- {
69
- write: async (message) => {
70
- const seq =
71
- this.feedMessages.push({
72
- timeframe: this.timeframe,
73
- payload: {
74
- data: message
75
- }
76
- }) - 1;
77
-
78
- await this._onConfirm.waitFor(() => this.confirmed >= seq);
79
- return {
80
- seq,
81
- feedKey: this.key
82
- };
83
- }
67
+ this.host = new DatabaseHost({
68
+ write: async (message) => {
69
+ const seq =
70
+ this.feedMessages.push({
71
+ timeframe: this.timeframe,
72
+ payload: {
73
+ data: message,
74
+ },
75
+ }) - 1;
76
+
77
+ await this._onConfirm.waitFor(() => this.confirmed >= seq);
78
+ return {
79
+ seq,
80
+ feedKey: this.key,
81
+ };
84
82
  },
85
- this.snapshot?.database
86
- );
83
+ });
84
+
87
85
  await this.host.open(this.hostItems, this.modelFactory);
86
+ if (this.snapshot) {
87
+ this.host._itemDemuxer.restoreFromSnapshot(this.snapshot.database);
88
+ }
88
89
 
89
90
  this.proxy = new DatabaseProxy(this.host.createDataServiceHost(), SPACE_KEY);
90
91
  this.items = new ItemManager(this.modelFactory);
@@ -109,7 +110,7 @@ export class DatabaseTestPeer {
109
110
  replicate(to?: Timeframe) {
110
111
  const toTimeframe = Timeframe.merge(
111
112
  to ?? new Timeframe(Array.from(this.rig.peers.values()).map((peer) => [peer.key, peer.confirmed])),
112
- this.timeframe
113
+ this.timeframe,
113
114
  );
114
115
  toTimeframe.set(this.key, this.confirmed);
115
116
 
@@ -133,7 +134,7 @@ export class DatabaseTestPeer {
133
134
  this.snapshot = {
134
135
  spaceKey: SPACE_KEY.asUint8Array(),
135
136
  database: this.host.createSnapshot(),
136
- timeframe: this.timeframe
137
+ timeframe: this.timeframe,
137
138
  };
138
139
  return this.snapshot;
139
140
  }
@@ -151,7 +152,7 @@ export class DatabaseTestPeer {
151
152
  message && {
152
153
  feedKey: peer.key,
153
154
  seq: seq + 1,
154
- data: message
155
+ data: message,
155
156
  }
156
157
  );
157
158
  })
@@ -186,8 +187,8 @@ export class DatabaseTestPeer {
186
187
  feedKey: candidate.feedKey,
187
188
  seq: candidate.seq,
188
189
  memberKey: candidate.feedKey,
189
- timeframe: candidate.data.timeframe
190
- }
190
+ timeframe: candidate.data.timeframe,
191
+ },
191
192
  });
192
193
  this.timeframe = Timeframe.merge(this.timeframe, new Timeframe([[candidate.feedKey, candidate.seq]]));
193
194
  }