@dxos/client-services 0.1.23 → 0.1.24

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 (115) hide show
  1. package/dist/lib/browser/chunk-R2FEBB66.mjs +3865 -0
  2. package/dist/lib/browser/chunk-R2FEBB66.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +56 -3066
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/packlets/testing/index.mjs +198 -0
  7. package/dist/lib/browser/packlets/testing/index.mjs.map +7 -0
  8. package/dist/lib/node/index.cjs +1401 -645
  9. package/dist/lib/node/index.cjs.map +4 -4
  10. package/dist/lib/node/meta.json +1 -1
  11. package/dist/lib/node/packlets/testing/index.cjs +3688 -0
  12. package/dist/lib/node/packlets/testing/index.cjs.map +7 -0
  13. package/dist/types/src/packlets/deprecated/profile.d.ts +3 -1
  14. package/dist/types/src/packlets/deprecated/profile.d.ts.map +1 -1
  15. package/dist/types/src/packlets/deprecated/space.d.ts.map +1 -1
  16. package/dist/types/src/packlets/deprecated/system.d.ts +15 -9
  17. package/dist/types/src/packlets/deprecated/system.d.ts.map +1 -1
  18. package/dist/types/src/packlets/devtools/devtools.d.ts +2 -2
  19. package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
  20. package/dist/types/src/packlets/devtools/feeds.d.ts.map +1 -1
  21. package/dist/types/src/packlets/devtools/network.d.ts +3 -3
  22. package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
  23. package/dist/types/src/packlets/devtools/spaces.d.ts.map +1 -1
  24. package/dist/types/src/packlets/identity/authenticator.d.ts +1 -1
  25. package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
  26. package/dist/types/src/packlets/identity/identity-manager.d.ts +7 -1
  27. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  28. package/dist/types/src/packlets/identity/identity.d.ts +1 -0
  29. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  30. package/dist/types/src/packlets/invitations/halo-invitations-handler.d.ts +11 -3
  31. package/dist/types/src/packlets/invitations/halo-invitations-handler.d.ts.map +1 -1
  32. package/dist/types/src/packlets/invitations/invitations.d.ts +1 -1
  33. package/dist/types/src/packlets/invitations/invitations.d.ts.map +1 -1
  34. package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts.map +1 -1
  35. package/dist/types/src/packlets/services/service-context.d.ts +2 -0
  36. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  37. package/dist/types/src/packlets/services/service-context.test.d.ts +2 -0
  38. package/dist/types/src/packlets/services/service-context.test.d.ts.map +1 -0
  39. package/dist/types/src/packlets/services/service-definitions.d.ts +1 -1
  40. package/dist/types/src/packlets/services/service-definitions.d.ts.map +1 -1
  41. package/dist/types/src/packlets/services/service-host.d.ts +9 -6
  42. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  43. package/dist/types/src/packlets/services/service-registry.d.ts +4 -3
  44. package/dist/types/src/packlets/services/service-registry.d.ts.map +1 -1
  45. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +9 -2
  46. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  47. package/dist/types/src/packlets/spaces/data-space.d.ts +18 -4
  48. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  49. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +50 -0
  50. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -0
  51. package/dist/types/src/packlets/spaces/notarization-plugin.test.d.ts +2 -0
  52. package/dist/types/src/packlets/spaces/notarization-plugin.test.d.ts.map +1 -0
  53. package/dist/types/src/packlets/spaces/spaces-service.d.ts +2 -1
  54. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  55. package/dist/types/src/packlets/testing/credential-utils.d.ts +8 -0
  56. package/dist/types/src/packlets/testing/credential-utils.d.ts.map +1 -0
  57. package/dist/types/src/packlets/testing/index.d.ts +2 -0
  58. package/dist/types/src/packlets/testing/index.d.ts.map +1 -1
  59. package/dist/types/src/packlets/testing/invitation-utils.d.ts +3 -0
  60. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -0
  61. package/dist/types/src/packlets/testing/test-builder.d.ts +2 -2
  62. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  63. package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts +28 -0
  64. package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts.map +1 -0
  65. package/dist/types/src/packlets/vault/{iframe-runtime.d.ts → iframe-proxy-runtime.d.ts} +4 -4
  66. package/dist/types/src/packlets/vault/iframe-proxy-runtime.d.ts.map +1 -0
  67. package/dist/types/src/packlets/vault/index.d.ts +4 -1
  68. package/dist/types/src/packlets/vault/index.d.ts.map +1 -1
  69. package/dist/types/src/packlets/vault/vault-resource-lock.d.ts +19 -0
  70. package/dist/types/src/packlets/vault/vault-resource-lock.d.ts.map +1 -0
  71. package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
  72. package/dist/types/src/packlets/vault/worker-session.d.ts +4 -4
  73. package/dist/types/src/packlets/vault/worker-session.d.ts.map +1 -1
  74. package/package.json +29 -29
  75. package/src/packlets/deprecated/profile.ts +20 -2
  76. package/src/packlets/deprecated/space.ts +41 -37
  77. package/src/packlets/deprecated/system.ts +26 -13
  78. package/src/packlets/devtools/devtools.ts +4 -4
  79. package/src/packlets/devtools/feeds.ts +11 -6
  80. package/src/packlets/devtools/network.ts +20 -9
  81. package/src/packlets/devtools/spaces.ts +7 -6
  82. package/src/packlets/identity/authenticator.ts +11 -6
  83. package/src/packlets/identity/identity-manager.test.ts +24 -15
  84. package/src/packlets/identity/identity-manager.ts +22 -8
  85. package/src/packlets/identity/identity.test.ts +21 -22
  86. package/src/packlets/identity/identity.ts +10 -5
  87. package/src/packlets/invitations/halo-invitations-handler.test.ts +4 -45
  88. package/src/packlets/invitations/halo-invitations-handler.ts +33 -17
  89. package/src/packlets/invitations/invitations.ts +2 -1
  90. package/src/packlets/invitations/space-invitations-handler.test.ts +43 -26
  91. package/src/packlets/invitations/space-invitations-handler.ts +8 -11
  92. package/src/packlets/services/service-context.test.ts +56 -0
  93. package/src/packlets/services/service-context.ts +60 -4
  94. package/src/packlets/services/service-definitions.ts +1 -1
  95. package/src/packlets/services/service-host.test.ts +74 -5
  96. package/src/packlets/services/service-host.ts +85 -23
  97. package/src/packlets/services/service-registry.test.ts +2 -1
  98. package/src/packlets/services/service-registry.ts +5 -1
  99. package/src/packlets/spaces/data-space-manager.test.ts +71 -14
  100. package/src/packlets/spaces/data-space-manager.ts +49 -8
  101. package/src/packlets/spaces/data-space.ts +113 -14
  102. package/src/packlets/spaces/notarization-plugin.test.ts +81 -0
  103. package/src/packlets/spaces/notarization-plugin.ts +211 -0
  104. package/src/packlets/spaces/spaces-service.ts +9 -1
  105. package/src/packlets/testing/credential-utils.ts +28 -0
  106. package/src/packlets/testing/index.ts +2 -0
  107. package/src/packlets/testing/invitation-utils.ts +44 -0
  108. package/src/packlets/testing/test-builder.ts +14 -18
  109. package/src/packlets/vault/iframe-host-runtime.ts +112 -0
  110. package/src/packlets/vault/{iframe-runtime.ts → iframe-proxy-runtime.ts} +3 -3
  111. package/src/packlets/vault/index.ts +4 -1
  112. package/src/packlets/vault/vault-resource-lock.ts +76 -0
  113. package/src/packlets/vault/worker-runtime.ts +13 -3
  114. package/src/packlets/vault/worker-session.ts +17 -17
  115. package/dist/types/src/packlets/vault/iframe-runtime.d.ts.map +0 -1
@@ -4,21 +4,23 @@
4
4
 
5
5
  import { Config } from '@dxos/config';
6
6
  import { raise } from '@dxos/debug';
7
+ import { DocumentModel } from '@dxos/document-model';
7
8
  import { DataServiceImpl } from '@dxos/echo-db';
8
9
  import { log } from '@dxos/log';
9
10
  import { ModelFactory } from '@dxos/model-factory';
10
11
  import { NetworkManager } from '@dxos/network-manager';
11
- import { ObjectModel } from '@dxos/object-model';
12
+ import { Status } from '@dxos/protocols/proto/dxos/client';
12
13
  import { Storage } from '@dxos/random-access-storage';
13
14
  import { TextModel } from '@dxos/text-model';
14
15
 
15
- import { SpaceServiceImpl, ProfileServiceImpl, SystemServiceImpl, TracingServiceImpl } from '../deprecated';
16
+ import { SpaceServiceImpl, ProfileServiceImpl, TracingServiceImpl, SystemServiceImpl } from '../deprecated';
16
17
  import { DevtoolsServiceImpl, DevtoolsHostEvents } from '../devtools';
17
18
  import { DevicesServiceImpl } from '../identity/devices-service-impl';
18
19
  import { HaloInvitationsServiceImpl, SpaceInvitationsServiceImpl } from '../invitations';
19
20
  import { NetworkServiceImpl } from '../network';
20
21
  import { SpacesServiceImpl } from '../spaces';
21
22
  import { createStorageObjects } from '../storage';
23
+ import { VaultResourceLock } from '../vault';
22
24
  import { ServiceContext } from './service-context';
23
25
  import { ClientServicesProvider, ClientServices, clientServiceBundle } from './service-definitions';
24
26
  import { ServiceRegistry } from './service-registry';
@@ -26,7 +28,7 @@ import { ServiceRegistry } from './service-registry';
26
28
  // TODO(burdon): Factor out to spaces.
27
29
  // TODO(burdon): Defaults (with TextModel).
28
30
  export const createDefaultModelFactory = () => {
29
- return new ModelFactory().registerModel(ObjectModel).registerModel(TextModel);
31
+ return new ModelFactory().registerModel(DocumentModel).registerModel(TextModel);
30
32
  };
31
33
 
32
34
  type ClientServicesHostParams = {
@@ -34,31 +36,77 @@ type ClientServicesHostParams = {
34
36
  modelFactory?: ModelFactory;
35
37
  networkManager: NetworkManager;
36
38
  storage?: Storage;
39
+ lockKey?: string;
37
40
  };
38
41
 
39
42
  /**
40
43
  * Remote service implementation.
41
44
  */
42
45
  export class ClientServicesHost implements ClientServicesProvider {
43
- private _serviceContext!: ServiceContext;
44
- private _serviceRegistry!: ServiceRegistry<ClientServices>;
46
+ private readonly _resourceLock?: VaultResourceLock;
47
+ private readonly _serviceRegistry: ServiceRegistry<ClientServices>;
48
+ private readonly _systemService: SystemServiceImpl;
45
49
 
46
50
  private readonly _config: Config;
47
51
  private readonly _modelFactory: ModelFactory;
48
52
  private readonly _networkManager: NetworkManager;
49
- private _storage: Storage;
53
+ private readonly _storage: Storage;
54
+
55
+ /**
56
+ * @internal
57
+ */
58
+ _serviceContext!: ServiceContext;
59
+ private _open = false;
50
60
 
51
61
  constructor({
52
62
  config,
53
63
  modelFactory = createDefaultModelFactory(),
54
64
  // TODO(burdon): Create ApolloLink abstraction (see Client).
55
65
  networkManager,
56
- storage = createStorageObjects(config.get('runtime.client.storage', {})!).storage
66
+ storage = createStorageObjects(config.get('runtime.client.storage', {})!).storage,
67
+ lockKey
57
68
  }: ClientServicesHostParams) {
58
69
  this._config = config;
59
70
  this._modelFactory = modelFactory;
60
71
  this._networkManager = networkManager;
61
72
  this._storage = storage;
73
+
74
+ this._resourceLock = lockKey
75
+ ? new VaultResourceLock({
76
+ lockKey,
77
+ onAcquire: () => this.open(),
78
+ onRelease: () => this.close()
79
+ })
80
+ : undefined;
81
+
82
+ this._systemService = new SystemServiceImpl({
83
+ config: this._config,
84
+
85
+ onInit: async () => {
86
+ await this._resourceLock?.acquire();
87
+ },
88
+
89
+ onStatus: async () => {
90
+ if (!this.isOpen) {
91
+ return { status: Status.INACTIVE };
92
+ }
93
+
94
+ return { status: Status.ACTIVE };
95
+ },
96
+
97
+ onReset: async () => {
98
+ await this._serviceContext?.reset();
99
+ }
100
+ });
101
+
102
+ // TODO(burdon): Start to think of DMG (dynamic services).
103
+ this._serviceRegistry = new ServiceRegistry<ClientServices>(clientServiceBundle, {
104
+ SystemService: this._systemService
105
+ });
106
+ }
107
+
108
+ get isOpen() {
109
+ return this._open;
62
110
  }
63
111
 
64
112
  get descriptors() {
@@ -70,27 +118,21 @@ export class ClientServicesHost implements ClientServicesProvider {
70
118
  }
71
119
 
72
120
  async open() {
73
- log('opening...');
74
- await this._initialize();
75
- await this._serviceContext.open();
76
- const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
77
- log('opened', { deviceKey });
78
- }
121
+ if (this._open) {
122
+ return;
123
+ }
79
124
 
80
- async close() {
81
- const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
82
- log('closing...', { deviceKey });
83
- await this._serviceContext.close();
84
- log('closed', { deviceKey });
85
- }
125
+ log('opening...');
86
126
 
87
- // TODO(burdon): Move into open.
88
- async _initialize() {
127
+ // TODO(wittjosiah): Make re-entrant.
89
128
  // TODO(burdon): Break into components.
90
129
  this._serviceContext = new ServiceContext(this._storage, this._networkManager, this._modelFactory);
91
130
 
92
131
  // TODO(burdon): Start to think of DMG (dynamic services).
93
- this._serviceRegistry = new ServiceRegistry<ClientServices>(clientServiceBundle, {
132
+ this._serviceRegistry.setServices({
133
+ // TODO(burdon): Move to new protobuf definitions.
134
+ SystemService: this._systemService,
135
+
94
136
  HaloInvitationsService: new HaloInvitationsServiceImpl(
95
137
  this._serviceContext.identityManager,
96
138
  this._serviceContext.haloInvitations
@@ -112,8 +154,10 @@ export class ClientServicesHost implements ClientServicesProvider {
112
154
 
113
155
  // TODO(burdon): Move to new protobuf definitions.
114
156
  ProfileService: new ProfileServiceImpl(this._serviceContext),
157
+
158
+ // TODO(burdon): Port old SubscribeSpaces to QueryServices>
115
159
  SpaceService: new SpaceServiceImpl(this._serviceContext),
116
- SystemService: new SystemServiceImpl(this._config, this._serviceContext),
160
+
117
161
  TracingService: new TracingServiceImpl(this._config),
118
162
  DevtoolsHost: new DevtoolsServiceImpl({
119
163
  events: new DevtoolsHostEvents(),
@@ -121,5 +165,23 @@ export class ClientServicesHost implements ClientServicesProvider {
121
165
  context: this._serviceContext
122
166
  })
123
167
  });
168
+
169
+ await this._serviceContext.open();
170
+ this._open = true;
171
+ const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
172
+ log('opened', { deviceKey });
173
+ }
174
+
175
+ async close() {
176
+ if (!this._open) {
177
+ return;
178
+ }
179
+
180
+ const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
181
+ log('closing...', { deviceKey });
182
+ this._serviceRegistry.setServices({ SystemService: this._systemService });
183
+ await this._serviceContext.close();
184
+ this._open = false;
185
+ log('closed', { deviceKey });
124
186
  }
125
187
  }
@@ -13,6 +13,7 @@ import { describe, test } from '@dxos/test';
13
13
 
14
14
  import { SpaceInvitationsProxy, SpaceInvitationsServiceImpl } from '../invitations';
15
15
  import { createServiceContext } from '../testing';
16
+ import { ClientServices } from './service-definitions';
16
17
  import { ServiceRegistry } from './service-registry';
17
18
 
18
19
  // TODO(burdon): Create TestService (that doesn't require peers).
@@ -54,7 +55,7 @@ describe('service registry', () => {
54
55
 
55
56
  const server = createProtoRpcPeer({
56
57
  exposed: serviceRegistry.descriptors,
57
- handlers: serviceRegistry.services,
58
+ handlers: serviceRegistry.services as ClientServices,
58
59
  port: serverPort
59
60
  });
60
61
 
@@ -11,7 +11,7 @@ export class ServiceRegistry<Services> {
11
11
  // prettier-ignore
12
12
  constructor (
13
13
  private readonly _serviceBundle: ServiceBundle<Services>,
14
- private readonly _handlers: Services
14
+ private _handlers: Partial<Services>
15
15
  ) {}
16
16
 
17
17
  get descriptors() {
@@ -21,4 +21,8 @@ export class ServiceRegistry<Services> {
21
21
  get services() {
22
22
  return this._handlers;
23
23
  }
24
+
25
+ setServices(services: Partial<Services>) {
26
+ this._handlers = services;
27
+ }
24
28
  }
@@ -5,16 +5,43 @@
5
5
  import { expect } from 'chai';
6
6
 
7
7
  import { createAdmissionCredentials } from '@dxos/credentials';
8
+ import { DocumentModel } from '@dxos/document-model';
8
9
  import { AuthStatus, DataServiceSubscriptions } from '@dxos/echo-db';
9
10
  import { writeMessages } from '@dxos/feed-store';
11
+ import { log } from '@dxos/log';
10
12
  import { ModelFactory } from '@dxos/model-factory';
11
- import { ObjectModel } from '@dxos/object-model';
12
13
  import { test, describe } from '@dxos/test';
13
14
 
14
15
  import { createSigningContext, syncItems, TestBuilder } from '../testing';
15
16
  import { DataSpaceManager } from './data-space-manager';
16
17
 
17
18
  describe('DataSpaceManager', () => {
19
+ test('create space', async () => {
20
+ const builder = new TestBuilder();
21
+
22
+ const peer = builder.createPeer();
23
+ const identity = await createSigningContext(peer.keyring);
24
+ const dataSpaceManager = new DataSpaceManager(
25
+ peer.spaceManager,
26
+ peer.metadataStore,
27
+ new DataServiceSubscriptions(),
28
+ peer.keyring,
29
+ identity,
30
+ new ModelFactory().registerModel(DocumentModel),
31
+ peer.feedStore,
32
+ peer.snapshotStore
33
+ );
34
+ const space = await dataSpaceManager.createSpace();
35
+
36
+ // Process all written mutations.
37
+ await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
38
+
39
+ expect(space.inner.spaceState.genesisCredential).to.exist;
40
+ expect(space.inner.spaceState.members.size).to.equal(1);
41
+ expect(space.inner.spaceState.feeds.size).to.equal(2);
42
+ expect(space.inner.protocol.feeds.size).to.equal(2);
43
+ });
44
+
18
45
  test('sync between peers', async () => {
19
46
  const builder = new TestBuilder();
20
47
 
@@ -26,7 +53,8 @@ describe('DataSpaceManager', () => {
26
53
  new DataServiceSubscriptions(),
27
54
  peer1.keyring,
28
55
  identity1,
29
- new ModelFactory().registerModel(ObjectModel),
56
+ new ModelFactory().registerModel(DocumentModel),
57
+ peer1.feedStore,
30
58
  peer1.snapshotStore
31
59
  );
32
60
 
@@ -38,17 +66,13 @@ describe('DataSpaceManager', () => {
38
66
  new DataServiceSubscriptions(),
39
67
  peer2.keyring,
40
68
  identity2,
41
- new ModelFactory().registerModel(ObjectModel),
69
+ new ModelFactory().registerModel(DocumentModel),
70
+ peer2.feedStore,
42
71
  peer1.snapshotStore
43
72
  );
44
73
 
45
74
  const space1 = await dataSpaceManager1.createSpace();
46
- const space2 = await dataSpaceManager2.acceptSpace({
47
- spaceKey: space1.key,
48
- genesisFeedKey: space1.inner.genesisFeedKey,
49
- controlFeedKey: await peer2.keyring.createKey(),
50
- dataFeedKey: await peer2.keyring.createKey()
51
- });
75
+ await space1.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
52
76
 
53
77
  // Admit peer2 to space1.
54
78
  await writeMessages(
@@ -56,15 +80,48 @@ describe('DataSpaceManager', () => {
56
80
  await createAdmissionCredentials(
57
81
  identity1.credentialSigner,
58
82
  identity2.identityKey,
59
- identity2.deviceKey,
60
83
  space1.key,
61
- space2.inner.controlFeedKey,
62
- space2.inner.dataFeedKey,
63
- space2.inner.genesisFeedKey
84
+ space1.inner.genesisFeedKey
64
85
  )
65
86
  );
66
87
 
67
- await syncItems(space1, space2);
88
+ // Accept must be called after admission so that the peer can authenticate for notarization.
89
+ const space2 = await dataSpaceManager2.acceptSpace({
90
+ spaceKey: space1.key,
91
+ genesisFeedKey: space1.inner.genesisFeedKey
92
+ });
93
+
94
+ log('', {
95
+ peer1: {
96
+ identity: identity1.identityKey,
97
+ device: identity1.deviceKey,
98
+ control: space1.inner.controlFeedKey,
99
+ data: space1.inner.dataFeedKey
100
+ },
101
+ peer2: {
102
+ identity: identity2.identityKey,
103
+ device: identity2.deviceKey,
104
+ control: space2.inner.controlFeedKey,
105
+ data: space2.inner.dataFeedKey
106
+ }
107
+ });
108
+
109
+ await space1.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
110
+ await space2.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
111
+
112
+ log('', {
113
+ space1: {
114
+ timeframe: space1.inner.controlPipeline.state.timeframe,
115
+ endTimeframe: space1.inner.controlPipeline.state.endTimeframe
116
+ },
117
+ space2: {
118
+ timeframe: space2.inner.controlPipeline.state.timeframe,
119
+ endTimeframe: space2.inner.controlPipeline.state.endTimeframe
120
+ }
121
+ });
122
+ log.break();
123
+
124
+ await syncItems(space1.dataPipelineController, space2.dataPipelineController);
68
125
 
69
126
  expect(space1.inner.protocol.sessions.get(identity2.deviceKey)).to.exist;
70
127
  expect(space1.inner.protocol.sessions.get(identity2.deviceKey)?.authStatus).to.equal(AuthStatus.SUCCESS);
@@ -6,8 +6,8 @@ import assert from 'node:assert';
6
6
 
7
7
  import { Event, synchronized, trackLeaks } from '@dxos/async';
8
8
  import { Context } from '@dxos/context';
9
+ import { getCredentialAssertion } from '@dxos/credentials';
9
10
  import {
10
- AcceptSpaceOptions,
11
11
  DataServiceSubscriptions,
12
12
  MetadataStore,
13
13
  SigningContext,
@@ -17,10 +17,12 @@ import {
17
17
  SpaceManager,
18
18
  SnapshotManager
19
19
  } from '@dxos/echo-db';
20
+ import { FeedStore } from '@dxos/feed-store';
20
21
  import { Keyring } from '@dxos/keyring';
21
22
  import { PublicKey } from '@dxos/keys';
22
23
  import { log } from '@dxos/log';
23
24
  import { ModelFactory } from '@dxos/model-factory';
25
+ import { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
24
26
  import { SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
25
27
  import { Presence } from '@dxos/teleport-extension-presence';
26
28
  import { ComplexMap, deferFunction } from '@dxos/util';
@@ -28,6 +30,13 @@ import { ComplexMap, deferFunction } from '@dxos/util';
28
30
  import { createAuthProvider } from '../identity';
29
31
  import { DataSpace } from './data-space';
30
32
 
33
+ const DATA_PIPELINE_READY_TIMEOUT = 3_000;
34
+
35
+ export type AcceptSpaceOptions = {
36
+ spaceKey: PublicKey;
37
+ genesisFeedKey: PublicKey;
38
+ };
39
+
31
40
  @trackLeaks('open', 'close')
32
41
  export class DataSpaceManager {
33
42
  private readonly _ctx = new Context();
@@ -43,6 +52,7 @@ export class DataSpaceManager {
43
52
  private readonly _keyring: Keyring,
44
53
  private readonly _signingContext: SigningContext,
45
54
  private readonly _modelFactory: ModelFactory,
55
+ private readonly _feedStore: FeedStore<FeedMessage>,
46
56
  private readonly _snapshotStore: SnapshotStore
47
57
  ) {}
48
58
 
@@ -59,10 +69,18 @@ export class DataSpaceManager {
59
69
  for (const spaceMetadata of this._metadataStore.spaces) {
60
70
  log('load space', { spaceMetadata });
61
71
  const space = await this._constructSpace(spaceMetadata);
62
- if (spaceMetadata.latestTimeframe) {
72
+ await space.initializeDataPipeline();
73
+ if (spaceMetadata.dataTimeframe) {
63
74
  log('waiting for latest timeframe', { spaceMetadata });
64
- await space.dataPipelineController.waitUntilTimeframe(spaceMetadata.latestTimeframe);
75
+ await space.dataPipelineController.pipelineState!.setTargetTimeframe(spaceMetadata.dataTimeframe);
65
76
  }
77
+ await space.dataPipelineController.pipelineState!.waitUntilReachedTargetTimeframe({
78
+ timeout: DATA_PIPELINE_READY_TIMEOUT
79
+ });
80
+ this._dataServiceSubscriptions.registerSpace(
81
+ space.key,
82
+ space.dataPipelineController.databaseBackend!.createDataServiceHost()
83
+ );
66
84
  }
67
85
  }
68
86
 
@@ -92,9 +110,19 @@ export class DataSpaceManager {
92
110
  log('creating space...', { spaceKey });
93
111
  const space = await this._constructSpace(metadata);
94
112
 
95
- await spaceGenesis(this._keyring, this._signingContext, space.inner);
113
+ const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner);
96
114
  await this._metadataStore.addSpace(metadata);
97
115
 
116
+ const memberCredential = credentials[1];
117
+ assert(getCredentialAssertion(memberCredential)['@type'] === 'dxos.halo.credentials.SpaceMember');
118
+ await this._signingContext.recordCredential(memberCredential);
119
+
120
+ await space.initializeDataPipeline();
121
+ this._dataServiceSubscriptions.registerSpace(
122
+ space.key,
123
+ space.dataPipelineController.databaseBackend!.createDataServiceHost()
124
+ );
125
+
98
126
  this.updated.emit();
99
127
  return space;
100
128
  }
@@ -106,13 +134,17 @@ export class DataSpaceManager {
106
134
 
107
135
  const metadata: SpaceMetadata = {
108
136
  key: opts.spaceKey,
109
- genesisFeedKey: opts.genesisFeedKey,
110
- controlFeedKey: opts.controlFeedKey,
111
- dataFeedKey: opts.dataFeedKey
137
+ genesisFeedKey: opts.genesisFeedKey
112
138
  };
113
139
 
114
140
  const space = await this._constructSpace(metadata);
115
141
  await this._metadataStore.addSpace(metadata);
142
+ await space.initializeDataPipeline();
143
+ this._dataServiceSubscriptions.registerSpace(
144
+ space.key,
145
+ space.dataPipelineController.databaseBackend!.createDataServiceHost()
146
+ );
147
+
116
148
  this.updated.emit();
117
149
  return space;
118
150
  }
@@ -126,6 +158,10 @@ export class DataSpaceManager {
126
158
  });
127
159
  const snapshotManager = new SnapshotManager(this._snapshotStore);
128
160
 
161
+ const controlFeed =
162
+ metadata.controlFeedKey && (await this._feedStore.openFeed(metadata.controlFeedKey, { writable: true }));
163
+ const dataFeed = metadata.dataFeedKey && (await this._feedStore.openFeed(metadata.dataFeedKey, { writable: true }));
164
+
129
165
  const space: Space = await this._spaceManager.constructSpace({
130
166
  metadata,
131
167
  swarmIdentity: {
@@ -139,8 +175,11 @@ export class DataSpaceManager {
139
175
  presence.createExtension({ remotePeerId: session.remotePeerId })
140
176
  );
141
177
  session.addExtension('dxos.mesh.teleport.objectsync', snapshotManager.objectSync.createExtension());
178
+ session.addExtension('dxos.mesh.teleport.notarization', dataSpace.notarizationPlugin.createExtension());
142
179
  }
143
180
  });
181
+ controlFeed && space.setControlFeed(controlFeed);
182
+ dataFeed && space.setDataFeed(dataFeed);
144
183
 
145
184
  const dataSpace = new DataSpace({
146
185
  inner: space,
@@ -149,11 +188,13 @@ export class DataSpaceManager {
149
188
  snapshotManager,
150
189
  presence,
151
190
  memberKey: this._signingContext.identityKey,
191
+ keyring: this._keyring,
192
+ feedStore: this._feedStore,
193
+ signingContext: this._signingContext,
152
194
  snapshotId: metadata.snapshot
153
195
  });
154
196
 
155
197
  await dataSpace.open();
156
- this._dataServiceSubscriptions.registerSpace(space.key, dataSpace.database.createDataServiceHost());
157
198
  this._spaces.set(metadata.key, dataSpace);
158
199
  return dataSpace;
159
200
  }