@dxos/client-services 0.4.9-main.4fd47cd → 0.4.9-main.7750cb2

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 (47) hide show
  1. package/dist/lib/browser/{chunk-MLGJV5VA.mjs → chunk-D6LYSGMU.mjs} +193 -254
  2. package/dist/lib/browser/chunk-D6LYSGMU.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1 -1
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/packlets/testing/index.mjs +4 -10
  6. package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
  7. package/dist/lib/node/{chunk-RRVRIFAG.cjs → chunk-FSN25IYL.cjs} +212 -273
  8. package/dist/lib/node/chunk-FSN25IYL.cjs.map +7 -0
  9. package/dist/lib/node/index.cjs +38 -38
  10. package/dist/lib/node/meta.json +1 -1
  11. package/dist/lib/node/packlets/testing/index.cjs +10 -17
  12. package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
  13. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  14. package/dist/types/src/packlets/services/diagnostics.d.ts.map +1 -1
  15. package/dist/types/src/packlets/services/service-context.d.ts +2 -3
  16. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  17. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  18. package/dist/types/src/packlets/spaces/automerge-space-state.d.ts +3 -0
  19. package/dist/types/src/packlets/spaces/automerge-space-state.d.ts.map +1 -1
  20. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +2 -3
  21. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  22. package/dist/types/src/packlets/spaces/data-space.d.ts +5 -3
  23. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  24. package/dist/types/src/packlets/spaces/spaces-service.d.ts +2 -3
  25. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  26. package/dist/types/src/packlets/testing/test-builder.d.ts +2 -6
  27. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  28. package/dist/types/src/version.d.ts +1 -1
  29. package/package.json +37 -37
  30. package/src/packlets/indexing/util.ts +2 -2
  31. package/src/packlets/invitations/space-invitation-protocol.test.ts +3 -12
  32. package/src/packlets/invitations/space-invitation-protocol.ts +0 -1
  33. package/src/packlets/services/automerge-host.test.ts +2 -5
  34. package/src/packlets/services/diagnostics.ts +2 -41
  35. package/src/packlets/services/service-context.test.ts +3 -76
  36. package/src/packlets/services/service-context.ts +5 -15
  37. package/src/packlets/services/service-host.ts +1 -5
  38. package/src/packlets/spaces/automerge-space-state.ts +9 -0
  39. package/src/packlets/spaces/data-space-manager.test.ts +3 -145
  40. package/src/packlets/spaces/data-space-manager.ts +3 -18
  41. package/src/packlets/spaces/data-space.ts +11 -43
  42. package/src/packlets/spaces/spaces-service.test.ts +4 -9
  43. package/src/packlets/spaces/spaces-service.ts +11 -15
  44. package/src/packlets/testing/test-builder.ts +2 -22
  45. package/src/version.ts +1 -1
  46. package/dist/lib/browser/chunk-MLGJV5VA.mjs.map +0 -7
  47. package/dist/lib/node/chunk-RRVRIFAG.cjs.map +0 -7
@@ -6,11 +6,8 @@ import { type ClientServices } from '@dxos/client-protocol';
6
6
  import { getFirstStreamValue } from '@dxos/codec-protobuf';
7
7
  import { type Config, type ConfigProto } from '@dxos/config';
8
8
  import { credentialTypeFilter } from '@dxos/credentials';
9
- import { DocumentModel, type DocumentModelState } from '@dxos/document-model';
10
- import { TYPE_PROPERTIES } from '@dxos/echo-db';
11
9
  import { invariant } from '@dxos/invariant';
12
10
  import { type PublicKey } from '@dxos/keys';
13
- import { log } from '@dxos/log';
14
11
  import { STORAGE_VERSION } from '@dxos/protocols';
15
12
  import {
16
13
  type Device,
@@ -180,25 +177,6 @@ export const createDiagnostics = async (
180
177
  return diagnostics;
181
178
  };
182
179
 
183
- const getProperties = (space: DataSpace) => {
184
- let properties: any = {};
185
- try {
186
- // Add properties to cache.
187
- const propertiesItem = space.dataPipeline.itemManager.items.find(
188
- (item) =>
189
- item.modelMeta?.type === DocumentModel.meta.type &&
190
- (item.state as DocumentModelState)?.type?.itemId === TYPE_PROPERTIES,
191
- );
192
-
193
- const state = propertiesItem?.state as DocumentModelState;
194
- properties = state?.data;
195
- } catch (err: any) {
196
- log.warn(err.message);
197
- }
198
-
199
- return properties;
200
- };
201
-
202
180
  const getSpaceStats = async (space: DataSpace): Promise<SpaceStats> => {
203
181
  const stats: SpaceStats = {
204
182
  key: space.key,
@@ -226,33 +204,16 @@ const getSpaceStats = async (space: DataSpace): Promise<SpaceStats> => {
226
204
 
227
205
  pipeline: {
228
206
  // TODO(burdon): Pick properties from credentials if needed.
229
- // currentEpoch: space.dataPipeline.currentEpoch,
230
- // appliedEpoch: space.dataPipeline.appliedEpoch,
207
+ currentEpoch: space.automergeSpaceState.lastEpoch,
208
+ appliedEpoch: space.automergeSpaceState.lastEpoch,
231
209
 
232
210
  controlFeeds: space.inner.controlPipeline.state.feeds.map((feed) => feed.key),
233
211
  currentControlTimeframe: space.inner.controlPipeline.state.timeframe,
234
212
  targetControlTimeframe: space.inner.controlPipeline.state.targetTimeframe,
235
213
  totalControlTimeframe: space.inner.controlPipeline.state.endTimeframe,
236
-
237
- // TODO(burdon): Empty?
238
- dataFeeds: space.dataPipeline.pipelineState?.feeds.map((feed) => feed.key) ?? [],
239
- startDataTimeframe: space.dataPipeline.pipelineState?.startTimeframe,
240
- currentDataTimeframe: space.dataPipeline.pipelineState?.timeframe,
241
- targetDataTimeframe: space.dataPipeline.pipelineState?.targetTimeframe,
242
- totalDataTimeframe: space.dataPipeline.pipelineState?.endTimeframe,
243
214
  },
244
215
  };
245
216
 
246
- // TODO(burdon): May not be open?
247
- if (space.dataPipeline.itemManager) {
248
- Object.assign(stats, {
249
- properties: getProperties(space),
250
- db: {
251
- objects: space.dataPipeline.itemManager.entities.size,
252
- },
253
- } as SpaceStats);
254
- }
255
-
256
217
  // TODO(burdon): Factor out.
257
218
  if (stats.metrics) {
258
219
  const { open, ready } = stats.metrics;
@@ -2,87 +2,14 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { expect } from 'chai';
6
-
7
- import { DocumentModel, type DocumentModelState, MutationBuilder } from '@dxos/document-model';
8
- import { createModelMutation, encodeModelMutation, genesisMutation } from '@dxos/echo-db';
9
- import { type WriteReceipt } from '@dxos/feed-store';
10
- import { PublicKey } from '@dxos/keys';
11
- import { log } from '@dxos/log';
12
5
  import { MemorySignalManagerContext } from '@dxos/messaging';
13
6
  import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
14
7
  import { describe, test } from '@dxos/test';
15
- import { Timeframe } from '@dxos/timeframe';
16
- import { range } from '@dxos/util';
17
8
 
18
- import { createServiceContext, syncItemsLocal } from '../testing';
9
+ import { createServiceContext } from '../testing';
19
10
  import { performInvitation } from '../testing/invitation-utils';
20
11
 
21
12
  describe('services/ServiceContext', () => {
22
- test('existing space is synchronized on device invitations', async () => {
23
- const networkContext = new MemorySignalManagerContext();
24
- const device1 = createServiceContext({ signalContext: networkContext });
25
- await device1.createIdentity();
26
- const space1 = await device1.dataSpaceManager!.createSpace();
27
-
28
- const itemId = PublicKey.random().toHex();
29
- await space1.dataPipeline.databaseHost!.getWriteStream()?.write({
30
- batch: genesisMutation(itemId, DocumentModel.meta.type),
31
- });
32
-
33
- let counter = 0;
34
-
35
- for (const _ in range(5)) {
36
- const receipts: WriteReceipt[] = [];
37
- for (const _ in range(50)) {
38
- receipts.push(
39
- await space1.dataPipeline.databaseHost!.getWriteStream()!.write({
40
- batch: createModelMutation(
41
- itemId,
42
- encodeModelMutation(DocumentModel.meta, new MutationBuilder().set('counter', ++counter).build()),
43
- ),
44
- }),
45
- );
46
- }
47
-
48
- await space1.dataPipeline.pipelineState!.waitUntilTimeframe(
49
- Timeframe.merge(...receipts.map((receipt) => new Timeframe([[receipt.feedKey, receipt.seq]]))),
50
- );
51
-
52
- await space1.createEpoch();
53
- log('epoch', { number: space1.dataPipeline.currentEpoch?.subject.assertion.number });
54
- }
55
-
56
- const device2 = createServiceContext({ signalContext: networkContext });
57
- await Promise.all(performInvitation({ host: device1, guest: device2, options: { kind: Invitation.Kind.DEVICE } }));
58
-
59
- await device2.dataSpaceManager!.waitUntilSpaceReady(space1!.key);
60
- const space2 = await device2.dataSpaceManager!.spaces.get(space1.key);
61
-
62
- log('peer 2', {
63
- currentEpoch: space2!.dataPipeline.currentEpoch?.subject.assertion.number,
64
- appliedEpoch: space2!.dataPipeline.appliedEpoch?.subject.assertion.number,
65
- });
66
-
67
- await space2?.dataPipeline.onNewEpoch.waitForCondition(
68
- () =>
69
- space2!.dataPipeline.appliedEpoch?.subject.assertion.number ===
70
- space1.dataPipeline.currentEpoch?.subject.assertion.number,
71
- );
72
-
73
- expect(space2!.dataPipeline.currentEpoch!.subject.assertion.number).to.equal(
74
- space1.dataPipeline.currentEpoch!.subject.assertion.number,
75
- );
76
- expect(space2!.dataPipeline.appliedEpoch!.subject.assertion.number).to.equal(
77
- space1.dataPipeline.appliedEpoch!.subject.assertion.number,
78
- );
79
-
80
- const item2 = space2!.dataPipeline.itemManager.entities.get(itemId);
81
- expect((item2!.state as DocumentModelState).data.counter).to.equal(counter);
82
-
83
- await syncItemsLocal(space1.dataPipeline, space2!.dataPipeline);
84
- });
85
-
86
13
  test('new space is synchronized on device invitations', async () => {
87
14
  const networkContext = new MemorySignalManagerContext();
88
15
  const device1 = createServiceContext({ signalContext: networkContext });
@@ -94,7 +21,7 @@ describe('services/ServiceContext', () => {
94
21
  const space1 = await device1.dataSpaceManager!.createSpace();
95
22
  await device2.dataSpaceManager!.waitUntilSpaceReady(space1!.key);
96
23
  const space2 = await device2.dataSpaceManager!.spaces.get(space1.key);
97
- await syncItemsLocal(space1.dataPipeline, space2!.dataPipeline);
24
+ await space2!.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.timeframe);
98
25
  }).tag('flaky');
99
26
 
100
27
  test('joined space is synchronized on device invitations', async () => {
@@ -118,6 +45,6 @@ describe('services/ServiceContext', () => {
118
45
 
119
46
  await device2.dataSpaceManager!.waitUntilSpaceReady(space1!.key);
120
47
  const space2 = await device2.dataSpaceManager!.spaces.get(space1.key);
121
- await syncItemsLocal(space1.dataPipeline, space2!.dataPipeline);
48
+ await space2!.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.timeframe);
122
49
  });
123
50
  });
@@ -4,16 +4,9 @@
4
4
 
5
5
  import { Trigger } from '@dxos/async';
6
6
  import { Context } from '@dxos/context';
7
- import { type CredentialProcessor, getCredentialAssertion } from '@dxos/credentials';
7
+ import { getCredentialAssertion, type CredentialProcessor } from '@dxos/credentials';
8
8
  import { failUndefined } from '@dxos/debug';
9
- import {
10
- valueEncoding,
11
- MetadataStore,
12
- SpaceManager,
13
- DataServiceSubscriptions,
14
- SnapshotStore,
15
- AutomergeHost,
16
- } from '@dxos/echo-pipeline';
9
+ import { AutomergeHost, MetadataStore, SnapshotStore, SpaceManager, valueEncoding } from '@dxos/echo-pipeline';
17
10
  import { FeedFactory, FeedStore } from '@dxos/feed-store';
18
11
  import { IndexMetadataStore, IndexStore, Indexer } from '@dxos/indexing';
19
12
  import { invariant } from '@dxos/invariant';
@@ -26,15 +19,15 @@ import { type NetworkManager } from '@dxos/network-manager';
26
19
  import { InvalidStorageVersionError, STORAGE_VERSION, trace } from '@dxos/protocols';
27
20
  import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
28
21
  import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
29
- import { type ProfileDocument, type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
22
+ import { type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
30
23
  import { type Storage } from '@dxos/random-access-storage';
31
24
  import { BlobStore } from '@dxos/teleport-extension-object-sync';
32
25
  import { trace as Trace } from '@dxos/tracing';
33
26
  import { safeInstanceof } from '@dxos/util';
34
27
 
35
28
  import {
36
- type CreateIdentityOptions,
37
29
  IdentityManager,
30
+ type CreateIdentityOptions,
38
31
  type IdentityManagerRuntimeParams,
39
32
  type JoinIdentityParams,
40
33
  } from '../identity';
@@ -42,8 +35,8 @@ import { createGetAllDocuments, createLoadDocuments } from '../indexing';
42
35
  import {
43
36
  DeviceInvitationProtocol,
44
37
  InvitationsHandler,
45
- type InvitationProtocol,
46
38
  SpaceInvitationProtocol,
39
+ type InvitationProtocol,
47
40
  } from '../invitations';
48
41
  import { DataSpaceManager, type DataSpaceManagerRuntimeParams, type SigningContext } from '../spaces';
49
42
 
@@ -57,7 +50,6 @@ export type ServiceContextRuntimeParams = IdentityManagerRuntimeParams & DataSpa
57
50
  @Trace.resource()
58
51
  export class ServiceContext {
59
52
  public readonly initialized = new Trigger();
60
- public readonly dataServiceSubscriptions = new DataServiceSubscriptions();
61
53
  public readonly metadataStore: MetadataStore;
62
54
  /**
63
55
  * @deprecated
@@ -186,7 +178,6 @@ export class ServiceContext {
186
178
  await this.feedStore.close();
187
179
  await this.networkManager.close();
188
180
  await this.signalManager.close();
189
- this.dataServiceSubscriptions.clear();
190
181
  await this.metadataStore.close();
191
182
  await this.indexer.destroy();
192
183
  log('closed');
@@ -246,7 +237,6 @@ export class ServiceContext {
246
237
  this.dataSpaceManager = new DataSpaceManager(
247
238
  this.spaceManager,
248
239
  this.metadataStore,
249
- this.dataServiceSubscriptions,
250
240
  this.keyring,
251
241
  signingContext,
252
242
  this.feedStore,
@@ -268,17 +268,13 @@ export class ClientServicesHost {
268
268
  SpacesService: new SpacesServiceImpl(
269
269
  this._serviceContext.identityManager,
270
270
  this._serviceContext.spaceManager,
271
- this._serviceContext.dataServiceSubscriptions,
272
271
  async () => {
273
272
  await this._serviceContext.initialized.wait();
274
273
  return this._serviceContext.dataSpaceManager!;
275
274
  },
276
275
  ),
277
276
 
278
- DataService: new DataServiceImpl(
279
- this._serviceContext.dataServiceSubscriptions,
280
- this._serviceContext.automergeHost,
281
- ),
277
+ DataService: new DataServiceImpl(this._serviceContext.automergeHost),
282
278
 
283
279
  IndexService: new IndexServiceImpl({
284
280
  indexer: this._serviceContext.indexer,
@@ -2,6 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { Event } from '@dxos/async';
5
6
  import { type CredentialProcessor, type SpecificCredential, checkCredentialType } from '@dxos/credentials';
6
7
  import { type Credential, type Epoch } from '@dxos/protocols/proto/dxos/halo/credentials';
7
8
 
@@ -9,6 +10,8 @@ export class AutomergeSpaceState implements CredentialProcessor {
9
10
  public rootUrl: string | undefined = undefined;
10
11
  public lastEpoch: SpecificCredential<Epoch> | undefined = undefined;
11
12
 
13
+ public readonly onNewEpoch = new Event<SpecificCredential<Epoch>>();
14
+
12
15
  private _isProcessingRootDocs = false;
13
16
 
14
17
  constructor(private readonly _onNewRoot: (rootUrl: string) => void) {}
@@ -26,6 +29,8 @@ export class AutomergeSpaceState implements CredentialProcessor {
26
29
  this._onNewRoot(this.rootUrl);
27
30
  }
28
31
  }
32
+
33
+ this.onNewEpoch.emit(credential);
29
34
  }
30
35
 
31
36
  startProcessingRootDocs() {
@@ -38,4 +43,8 @@ export class AutomergeSpaceState implements CredentialProcessor {
38
43
  }
39
44
  this._isProcessingRootDocs = true;
40
45
  }
46
+
47
+ async ensureEpochInitialized() {
48
+ await this.onNewEpoch.waitForCondition(() => !!this.lastEpoch);
49
+ }
41
50
  }
@@ -3,24 +3,16 @@
3
3
  //
4
4
 
5
5
  import { expect } from 'chai';
6
- import path from 'node:path';
7
6
 
8
7
  import { asyncTimeout, latch } from '@dxos/async';
9
- import { type SpecificCredential, createAdmissionCredentials } from '@dxos/credentials';
8
+ import { createAdmissionCredentials } from '@dxos/credentials';
10
9
  import { AuthStatus } from '@dxos/echo-pipeline';
11
- import { testLocalDatabase } from '@dxos/echo-pipeline/testing';
12
10
  import { writeMessages } from '@dxos/feed-store';
13
- import { type PublicKey } from '@dxos/keys';
14
11
  import { log } from '@dxos/log';
15
12
  import { SpaceState } from '@dxos/protocols/proto/dxos/client/services';
16
- import { type SpaceSnapshot } from '@dxos/protocols/proto/dxos/echo/snapshot';
17
- import { type Epoch } from '@dxos/protocols/proto/dxos/halo/credentials';
18
- import { StorageType } from '@dxos/random-access-storage';
19
- import { afterTest, describe, openAndClose, test } from '@dxos/test';
20
- import { Timeframe } from '@dxos/timeframe';
21
- import { range } from '@dxos/util';
13
+ import { describe, openAndClose, test } from '@dxos/test';
22
14
 
23
- import { TestBuilder, syncItemsLocal } from '../testing';
15
+ import { TestBuilder } from '../testing';
24
16
 
25
17
  describe('DataSpaceManager', () => {
26
18
  test('create space', async () => {
@@ -103,8 +95,6 @@ describe('DataSpaceManager', () => {
103
95
  });
104
96
  log.break();
105
97
 
106
- await syncItemsLocal(space1.dataPipeline, space2.dataPipeline);
107
-
108
98
  expect(space1.inner.protocol.sessions.get(peer2.identity.deviceKey)).to.exist;
109
99
  expect(space1.inner.protocol.sessions.get(peer2.identity.deviceKey)?.authStatus).to.equal(AuthStatus.SUCCESS);
110
100
  expect(space2.inner.protocol.sessions.get(peer1.identity.deviceKey)).to.exist;
@@ -157,137 +147,6 @@ describe('DataSpaceManager', () => {
157
147
  await receivedMessage();
158
148
  });
159
149
 
160
- describe('Epochs', () => {
161
- test
162
- .skip('Epoch truncates feeds', async () => {
163
- const builder = new TestBuilder();
164
- afterTest(async () => builder.destroy());
165
-
166
- const peer = builder.createPeer({
167
- dataStore: typeof window === 'undefined' ? StorageType.NODE : StorageType.WEBFS,
168
- });
169
- await peer.createIdentity();
170
- await openAndClose(peer.dataSpaceManager);
171
- const space = await peer.dataSpaceManager.createSpace();
172
- await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
173
-
174
- const feedDataPath = path.join(space.inner.dataPipeline.pipelineState!.feeds[0].key.toHex(), 'data');
175
- const directory = peer.storage.createDirectory('feeds');
176
- const file = directory.getOrCreateFile(feedDataPath);
177
- afterTest(() => file.close());
178
-
179
- expect((await file.stat()).size === 0).to.be.true;
180
-
181
- for (const _ in range(10)) {
182
- await testLocalDatabase(space.dataPipeline);
183
- }
184
-
185
- expect((await file.stat()).size !== 0).to.be.true;
186
- await space.createEpoch();
187
- expect((await file.stat()).size === 0).to.be.true;
188
- })
189
- .onlyEnvironments('nodejs', 'chromium', 'firefox')
190
- .tag('flaky');
191
-
192
- test('Loads only last epoch', async () => {
193
- const builder = new TestBuilder();
194
- afterTest(async () => builder.destroy());
195
-
196
- const peer = builder.createPeer();
197
- await peer.createIdentity();
198
- const epochsNumber = 10;
199
- let spaceKey: PublicKey;
200
- {
201
- // Create space and create epochs in it.s
202
- await openAndClose(peer.dataSpaceManager);
203
-
204
- const space = await peer.dataSpaceManager.createSpace();
205
- spaceKey = space.key;
206
- await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
207
-
208
- for (const _ of range(epochsNumber)) {
209
- await testLocalDatabase(space.dataPipeline);
210
- await space.createEpoch();
211
- }
212
- await space.close();
213
- await peer.dataSpaceManager.close();
214
- peer.props.dataSpaceManager = undefined;
215
- }
216
- {
217
- // Load same space and check if it loads only last epoch.s
218
- await openAndClose(peer.dataSpaceManager);
219
-
220
- const space = peer.dataSpaceManager.spaces.get(spaceKey)!;
221
-
222
- const epochs: number[] = [];
223
- space.dataPipeline.onNewEpoch.on((epoch: SpecificCredential<Epoch>) => {
224
- epochs.push(epoch.subject.assertion.number);
225
- });
226
- const processedFirstEpoch = space.dataPipeline.onNewEpoch.waitFor(() => true);
227
-
228
- await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
229
-
230
- await processedFirstEpoch;
231
- expect(epochs).to.deep.equal([epochsNumber]);
232
- }
233
- });
234
-
235
- test('Items are cleared before epoch applied', async () => {
236
- const builder = new TestBuilder();
237
- afterTest(async () => builder.destroy());
238
- const peer = builder.createPeer();
239
- await peer.createIdentity();
240
- await openAndClose(peer.dataSpaceManager);
241
-
242
- // Create space and fill it with Items.
243
- const space = await peer.dataSpaceManager.createSpace();
244
- await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
245
- const itemsNumber = 2;
246
- for (const _ of range(itemsNumber)) {
247
- await testLocalDatabase(space.dataPipeline);
248
- }
249
-
250
- // Create empty Epoch and check if it clears items.
251
- {
252
- const processedFirstEpoch = space.dataPipeline.onNewEpoch.waitFor(
253
- (epoch) => epoch.subject.assertion.number === 1,
254
- );
255
-
256
- // Empty snapshot.
257
- const snapshot: SpaceSnapshot = {
258
- spaceKey: space.key.asUint8Array(),
259
- timeframe: space.inner.dataPipeline.pipelineState!.timeframe,
260
- database: {},
261
- };
262
-
263
- const snapshotCid = await peer.snapshotStore.saveSnapshot(snapshot);
264
-
265
- const epoch: Epoch = {
266
- previousId: space.dataPipeline.currentEpoch?.id,
267
- timeframe: space.inner.dataPipeline.pipelineState!.timeframe,
268
- number: (space.dataPipeline.currentEpoch?.subject.assertion as Epoch).number + 1,
269
- snapshotCid,
270
- };
271
-
272
- const receipt = await space.inner.controlPipeline.writer.write({
273
- credential: {
274
- credential: await peer.props.signingContext!.credentialSigner.createCredential({
275
- subject: space.key,
276
- assertion: {
277
- '@type': 'dxos.halo.credentials.Epoch',
278
- ...epoch,
279
- },
280
- }),
281
- },
282
- });
283
- await space.inner.controlPipeline.state.waitUntilTimeframe(new Timeframe([[receipt.feedKey, receipt.seq]]));
284
- await processedFirstEpoch;
285
- }
286
-
287
- expect(Array.from(space.dataPipeline.itemManager.entities.keys()).length).to.equal(0);
288
- });
289
- });
290
-
291
150
  describe('activation', () => {
292
151
  test('can activate and deactivate a space', async () => {
293
152
  const builder = new TestBuilder();
@@ -308,7 +167,6 @@ describe('DataSpaceManager', () => {
308
167
  space.stateUpdate.waitForCondition(() => space.state === SpaceState.READY),
309
168
  500,
310
169
  );
311
- await testLocalDatabase(space.dataPipeline);
312
170
  });
313
171
  });
314
172
  });
@@ -3,15 +3,9 @@
3
3
  //
4
4
 
5
5
  import { Event, synchronized, trackLeaks } from '@dxos/async';
6
- import { cancelWithContext, Context } from '@dxos/context';
7
- import { type CredentialSigner, getCredentialAssertion } from '@dxos/credentials';
8
- import {
9
- type AutomergeHost,
10
- type DataServiceSubscriptions,
11
- type MetadataStore,
12
- type Space,
13
- type SpaceManager,
14
- } from '@dxos/echo-pipeline';
6
+ import { Context, cancelWithContext } from '@dxos/context';
7
+ import { getCredentialAssertion, type CredentialSigner } from '@dxos/credentials';
8
+ import { type AutomergeHost, type MetadataStore, type Space, type SpaceManager } from '@dxos/echo-pipeline';
15
9
  import { type FeedStore } from '@dxos/feed-store';
16
10
  import { invariant } from '@dxos/invariant';
17
11
  import { type Keyring } from '@dxos/keyring';
@@ -80,7 +74,6 @@ export class DataSpaceManager {
80
74
  constructor(
81
75
  private readonly _spaceManager: SpaceManager,
82
76
  private readonly _metadataStore: MetadataStore,
83
- private readonly _dataServiceSubscriptions: DataServiceSubscriptions,
84
77
  private readonly _keyring: Keyring,
85
78
  private readonly _signingContext: SigningContext,
86
79
  private readonly _feedStore: FeedStore<FeedMessage>,
@@ -270,10 +263,6 @@ export class DataSpaceManager {
270
263
  callbacks: {
271
264
  beforeReady: async () => {
272
265
  log('before space ready', { space: space.key });
273
- await this._dataServiceSubscriptions.registerSpace(
274
- space.key,
275
- dataSpace.dataPipeline.databaseHost!.createDataServiceHost(),
276
- );
277
266
  },
278
267
  afterReady: async () => {
279
268
  log('after space ready', { space: space.key, open: this._isOpen });
@@ -283,7 +272,6 @@ export class DataSpaceManager {
283
272
  },
284
273
  beforeClose: async () => {
285
274
  log('before space close', { space: space.key });
286
- await this._dataServiceSubscriptions.unregisterSpace(space.key);
287
275
  },
288
276
  },
289
277
  cache: metadata.cache,
@@ -297,9 +285,6 @@ export class DataSpaceManager {
297
285
  if (metadata.controlTimeframe) {
298
286
  dataSpace.inner.controlPipeline.state.setTargetTimeframe(metadata.controlTimeframe);
299
287
  }
300
- if (metadata.dataTimeframe) {
301
- dataSpace.dataPipeline.setTargetTimeframe(metadata.dataTimeframe);
302
- }
303
288
 
304
289
  this._spaces.set(metadata.key, dataSpace);
305
290
  return dataSpace;
@@ -6,14 +6,7 @@ import { Event, asyncTimeout, scheduleTask, sleep, synchronized, trackLeaks } fr
6
6
  import { AUTH_TIMEOUT } from '@dxos/client-protocol';
7
7
  import { cancelWithContext, Context, ContextDisposedError } from '@dxos/context';
8
8
  import { timed, warnAfterTimeout } from '@dxos/debug';
9
- import {
10
- type MetadataStore,
11
- type Space,
12
- createMappedFeedWriter,
13
- type DataPipeline,
14
- type CreateEpochOptions,
15
- type AutomergeHost,
16
- } from '@dxos/echo-pipeline';
9
+ import { type MetadataStore, type Space, createMappedFeedWriter, type AutomergeHost } from '@dxos/echo-pipeline';
17
10
  import { type FeedStore } from '@dxos/feed-store';
18
11
  import { failedInvariant, invariant } from '@dxos/invariant';
19
12
  import { type Keyring } from '@dxos/keyring';
@@ -71,11 +64,9 @@ export type DataSpaceParams = {
71
64
  automergeHost: AutomergeHost;
72
65
  };
73
66
 
74
- /**
75
- * Delete feed blocks after an epoch is created.
76
- */
77
- // TODO(dmaretskyi): Disabled till better times https://github.com/dxos/dxos/issues/3949.
78
- const ENABLE_FEED_PURGE = false;
67
+ export type CreateEpochOptions = {
68
+ migration?: CreateEpochRequest.Migration;
69
+ };
79
70
 
80
71
  @trackLeaks('open', 'close')
81
72
  @trace.resource()
@@ -160,10 +151,6 @@ export class DataSpace {
160
151
  return this._inner;
161
152
  }
162
153
 
163
- get dataPipeline(): DataPipeline {
164
- return this._inner.dataPipeline;
165
- }
166
-
167
154
  get presence() {
168
155
  return this._presence;
169
156
  }
@@ -278,20 +265,8 @@ export class DataSpace {
278
265
 
279
266
  this._automergeSpaceState.startProcessingRootDocs();
280
267
 
281
- await this._inner.initializeDataPipeline();
282
-
283
- this.metrics.dataPipelineOpen = new Date();
284
-
285
268
  // Wait for the first epoch.
286
- await cancelWithContext(this._ctx, this._inner.dataPipeline.ensureEpochInitialized());
287
-
288
- log('waiting for data pipeline to reach target timeframe');
289
- // Wait for the data pipeline to catch up to its desired timeframe.
290
- await this._inner.dataPipeline.pipelineState!.waitUntilReachedTargetTimeframe({
291
- ctx: this._ctx,
292
- breakOnStall: false,
293
- });
294
- this.metrics.dataPipelineReady = new Date();
269
+ await cancelWithContext(this._ctx, this.automergeSpaceState.ensureEpochInitialized());
295
270
 
296
271
  log('data pipeline ready');
297
272
  await this._callbacks.beforeReady?.();
@@ -426,7 +401,12 @@ export class DataSpace {
426
401
  case CreateEpochRequest.Migration.NONE:
427
402
  {
428
403
  // TODO(dmaretskyi): Unify epoch construction.
429
- epoch = await this.dataPipeline.createEpoch();
404
+ epoch = {
405
+ previousId: this._automergeSpaceState.lastEpoch?.id,
406
+ number: (this._automergeSpaceState.lastEpoch?.subject.assertion.number ?? -1) + 1,
407
+ timeframe: this._automergeSpaceState.lastEpoch?.subject.assertion.timeframe ?? new Timeframe(),
408
+ automergeRoot: this._automergeSpaceState.lastEpoch?.subject.assertion?.automergeRoot,
409
+ };
430
410
  }
431
411
  break;
432
412
  case CreateEpochRequest.Migration.INIT_AUTOMERGE:
@@ -476,18 +456,6 @@ export class DataSpace {
476
456
  });
477
457
 
478
458
  await this.inner.controlPipeline.state.waitUntilTimeframe(new Timeframe([[receipt.feedKey, receipt.seq]]));
479
-
480
- // Clear feed blocks before epoch.
481
- if (ENABLE_FEED_PURGE) {
482
- for (const feed of this.inner.dataPipeline.pipelineState?.feeds ?? []) {
483
- const indexBeforeEpoch = epoch.timeframe.get(feed.key);
484
- if (indexBeforeEpoch === undefined) {
485
- continue;
486
- }
487
-
488
- await feed.safeClear(0, indexBeforeEpoch + 1);
489
- }
490
- }
491
459
  }
492
460
 
493
461
  @synchronized
@@ -24,15 +24,10 @@ describe('SpacesService', () => {
24
24
  beforeEach(async () => {
25
25
  serviceContext = createServiceContext();
26
26
  await serviceContext.open(new Context());
27
- spacesService = new SpacesServiceImpl(
28
- serviceContext.identityManager,
29
- serviceContext.spaceManager,
30
- serviceContext.dataServiceSubscriptions,
31
- async () => {
32
- await serviceContext.initialized.wait();
33
- return serviceContext.dataSpaceManager!;
34
- },
35
- );
27
+ spacesService = new SpacesServiceImpl(serviceContext.identityManager, serviceContext.spaceManager, async () => {
28
+ await serviceContext.initialized.wait();
29
+ return serviceContext.dataSpaceManager!;
30
+ });
36
31
  });
37
32
 
38
33
  afterEach(async () => {