@dxos/client-services 0.1.52 → 0.1.53-main.01cbc6b
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/lib/browser/{chunk-C3SPTMW3.mjs → chunk-EYXOVYW5.mjs} +817 -694
- package/dist/lib/browser/chunk-EYXOVYW5.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +101 -120
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +24 -7
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +884 -782
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +801 -664
- package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
- package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +7 -1
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +11 -2
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +2 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +2 -2
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +7 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts +1 -1
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/iframe-proxy-runtime.d.ts +1 -1
- package/dist/types/src/packlets/vault/iframe-proxy-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/shell-runtime.d.ts +1 -21
- package/dist/types/src/packlets/vault/shell-runtime.d.ts.map +1 -1
- package/package.json +33 -32
- package/src/packlets/devtools/devtools.ts +2 -2
- package/src/packlets/identity/identity-manager.test.ts +1 -1
- package/src/packlets/identity/identity-manager.ts +8 -8
- package/src/packlets/identity/identity-service.ts +2 -2
- package/src/packlets/identity/identity.test.ts +1 -1
- package/src/packlets/identity/identity.ts +22 -27
- package/src/packlets/invitations/device-invitation-protocol.ts +4 -4
- package/src/packlets/invitations/invitation-extension.ts +5 -5
- package/src/packlets/invitations/invitations-handler.ts +6 -6
- package/src/packlets/invitations/invitations-service.ts +3 -3
- package/src/packlets/invitations/space-invitation-protocol.ts +9 -9
- package/src/packlets/locks/node.ts +2 -2
- package/src/packlets/logging/logging-service.ts +4 -4
- package/src/packlets/services/service-context.test.ts +1 -1
- package/src/packlets/services/service-context.ts +12 -10
- package/src/packlets/services/service-host.test.ts +50 -2
- package/src/packlets/services/service-host.ts +28 -10
- package/src/packlets/spaces/data-space-manager.test.ts +72 -118
- package/src/packlets/spaces/data-space-manager.ts +18 -9
- package/src/packlets/spaces/data-space.ts +66 -20
- package/src/packlets/spaces/notarization-plugin.test.ts +1 -1
- package/src/packlets/spaces/notarization-plugin.ts +9 -5
- package/src/packlets/spaces/spaces-service.ts +46 -32
- package/src/packlets/testing/invitation-utils.ts +2 -2
- package/src/packlets/testing/test-builder.ts +36 -4
- package/src/packlets/vault/iframe-host-runtime.ts +2 -2
- package/src/packlets/vault/iframe-proxy-runtime.ts +2 -2
- package/src/packlets/vault/shell-runtime.ts +3 -49
- package/src/packlets/vault/worker-runtime.ts +1 -1
- package/src/packlets/vault/worker-session.ts +2 -2
- package/dist/lib/browser/chunk-C3SPTMW3.mjs.map +0 -7
|
@@ -5,38 +5,30 @@
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
|
|
8
|
-
import { latch } from '@dxos/async';
|
|
8
|
+
import { asyncTimeout, latch } from '@dxos/async';
|
|
9
9
|
import { SpecificCredential, createAdmissionCredentials } from '@dxos/credentials';
|
|
10
|
-
import { AuthStatus
|
|
10
|
+
import { AuthStatus } from '@dxos/echo-pipeline';
|
|
11
11
|
import { testLocalDatabase } from '@dxos/echo-pipeline/testing';
|
|
12
12
|
import { writeMessages } from '@dxos/feed-store';
|
|
13
13
|
import { PublicKey } from '@dxos/keys';
|
|
14
14
|
import { log } from '@dxos/log';
|
|
15
|
+
import { SpaceState } from '@dxos/protocols/proto/dxos/client/services';
|
|
15
16
|
import { Epoch } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
16
17
|
import { StorageType } from '@dxos/random-access-storage';
|
|
17
|
-
import { afterTest, describe, test } from '@dxos/test';
|
|
18
|
+
import { afterTest, describe, openAndClose, test } from '@dxos/test';
|
|
18
19
|
import { range } from '@dxos/util';
|
|
19
20
|
|
|
20
|
-
import {
|
|
21
|
-
import { DataSpaceManager } from './data-space-manager';
|
|
21
|
+
import { TestBuilder, syncItemsLocal } from '../testing';
|
|
22
22
|
|
|
23
23
|
describe('DataSpaceManager', () => {
|
|
24
24
|
test('create space', async () => {
|
|
25
25
|
const builder = new TestBuilder();
|
|
26
26
|
|
|
27
27
|
const peer = builder.createPeer();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
new DataServiceSubscriptions(),
|
|
33
|
-
peer.keyring,
|
|
34
|
-
identity,
|
|
35
|
-
peer.feedStore,
|
|
36
|
-
);
|
|
37
|
-
await dataSpaceManager.open();
|
|
38
|
-
afterTest(() => dataSpaceManager.close());
|
|
39
|
-
const space = await dataSpaceManager.createSpace();
|
|
28
|
+
await peer.createIdentity();
|
|
29
|
+
await openAndClose(peer.dataSpaceManager);
|
|
30
|
+
|
|
31
|
+
const space = await peer.dataSpaceManager.createSpace();
|
|
40
32
|
|
|
41
33
|
// Process all written mutations.
|
|
42
34
|
await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
|
|
@@ -51,62 +43,44 @@ describe('DataSpaceManager', () => {
|
|
|
51
43
|
const builder = new TestBuilder();
|
|
52
44
|
|
|
53
45
|
const peer1 = builder.createPeer();
|
|
54
|
-
|
|
55
|
-
const dataSpaceManager1 = new DataSpaceManager(
|
|
56
|
-
peer1.spaceManager,
|
|
57
|
-
peer1.metadataStore,
|
|
58
|
-
new DataServiceSubscriptions(),
|
|
59
|
-
peer1.keyring,
|
|
60
|
-
identity1,
|
|
61
|
-
peer1.feedStore,
|
|
62
|
-
);
|
|
63
|
-
await dataSpaceManager1.open();
|
|
64
|
-
afterTest(() => dataSpaceManager1.close());
|
|
46
|
+
await peer1.createIdentity();
|
|
65
47
|
|
|
66
48
|
const peer2 = builder.createPeer();
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
peer2.metadataStore,
|
|
71
|
-
new DataServiceSubscriptions(),
|
|
72
|
-
peer2.keyring,
|
|
73
|
-
identity2,
|
|
74
|
-
peer2.feedStore,
|
|
75
|
-
);
|
|
76
|
-
await dataSpaceManager2.open();
|
|
77
|
-
afterTest(() => dataSpaceManager2.close());
|
|
49
|
+
await peer2.createIdentity();
|
|
50
|
+
|
|
51
|
+
await openAndClose(peer1.dataSpaceManager, peer2.dataSpaceManager);
|
|
78
52
|
|
|
79
|
-
const space1 = await
|
|
53
|
+
const space1 = await peer1.dataSpaceManager.createSpace();
|
|
80
54
|
await space1.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
|
|
81
55
|
|
|
82
56
|
// Admit peer2 to space1.
|
|
83
57
|
await writeMessages(
|
|
84
58
|
space1.inner.controlPipeline.writer,
|
|
85
59
|
await createAdmissionCredentials(
|
|
86
|
-
|
|
87
|
-
|
|
60
|
+
peer1.identity.credentialSigner,
|
|
61
|
+
peer2.identity.identityKey,
|
|
88
62
|
space1.key,
|
|
89
63
|
space1.inner.genesisFeedKey,
|
|
90
64
|
),
|
|
91
65
|
);
|
|
92
66
|
|
|
93
67
|
// Accept must be called after admission so that the peer can authenticate for notarization.
|
|
94
|
-
const space2 = await
|
|
68
|
+
const space2 = await peer2.dataSpaceManager.acceptSpace({
|
|
95
69
|
spaceKey: space1.key,
|
|
96
70
|
genesisFeedKey: space1.inner.genesisFeedKey,
|
|
97
71
|
});
|
|
98
|
-
await
|
|
72
|
+
await peer2.dataSpaceManager.waitUntilSpaceReady(space2.key);
|
|
99
73
|
|
|
100
74
|
log('', {
|
|
101
75
|
peer1: {
|
|
102
|
-
identity:
|
|
103
|
-
device:
|
|
76
|
+
identity: peer1.identity.identityKey,
|
|
77
|
+
device: peer1.identity.deviceKey,
|
|
104
78
|
control: space1.inner.controlFeedKey,
|
|
105
79
|
data: space1.inner.dataFeedKey,
|
|
106
80
|
},
|
|
107
81
|
peer2: {
|
|
108
|
-
identity:
|
|
109
|
-
device:
|
|
82
|
+
identity: peer2.identity.identityKey,
|
|
83
|
+
device: peer2.identity.deviceKey,
|
|
110
84
|
control: space2.inner.controlFeedKey,
|
|
111
85
|
data: space2.inner.dataFeedKey,
|
|
112
86
|
},
|
|
@@ -129,64 +103,47 @@ describe('DataSpaceManager', () => {
|
|
|
129
103
|
|
|
130
104
|
await syncItemsLocal(space1.dataPipeline, space2.dataPipeline);
|
|
131
105
|
|
|
132
|
-
expect(space1.inner.protocol.sessions.get(
|
|
133
|
-
expect(space1.inner.protocol.sessions.get(
|
|
134
|
-
expect(space2.inner.protocol.sessions.get(
|
|
135
|
-
expect(space2.inner.protocol.sessions.get(
|
|
106
|
+
expect(space1.inner.protocol.sessions.get(peer2.identity.deviceKey)).to.exist;
|
|
107
|
+
expect(space1.inner.protocol.sessions.get(peer2.identity.deviceKey)?.authStatus).to.equal(AuthStatus.SUCCESS);
|
|
108
|
+
expect(space2.inner.protocol.sessions.get(peer1.identity.deviceKey)).to.exist;
|
|
109
|
+
expect(space2.inner.protocol.sessions.get(peer1.identity.deviceKey)?.authStatus).to.equal(AuthStatus.SUCCESS);
|
|
136
110
|
});
|
|
137
111
|
|
|
138
112
|
test('pub/sub API', async () => {
|
|
139
113
|
const builder = new TestBuilder();
|
|
140
114
|
|
|
141
115
|
const peer1 = builder.createPeer();
|
|
142
|
-
|
|
143
|
-
const dataSpaceManager1 = new DataSpaceManager(
|
|
144
|
-
peer1.spaceManager,
|
|
145
|
-
peer1.metadataStore,
|
|
146
|
-
new DataServiceSubscriptions(),
|
|
147
|
-
peer1.keyring,
|
|
148
|
-
identity1,
|
|
149
|
-
peer1.feedStore,
|
|
150
|
-
);
|
|
151
|
-
await dataSpaceManager1.open();
|
|
152
|
-
afterTest(() => dataSpaceManager1.close());
|
|
116
|
+
await peer1.createIdentity();
|
|
153
117
|
|
|
154
118
|
const peer2 = builder.createPeer();
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
new DataServiceSubscriptions(),
|
|
160
|
-
peer2.keyring,
|
|
161
|
-
identity2,
|
|
162
|
-
peer2.feedStore,
|
|
163
|
-
);
|
|
164
|
-
await dataSpaceManager2.open();
|
|
165
|
-
afterTest(() => dataSpaceManager2.close());
|
|
119
|
+
await peer2.createIdentity();
|
|
120
|
+
await peer2.dataSpaceManager.open();
|
|
121
|
+
|
|
122
|
+
await openAndClose(peer1.dataSpaceManager, peer2.dataSpaceManager);
|
|
166
123
|
|
|
167
|
-
const space1 = await
|
|
124
|
+
const space1 = await peer1.dataSpaceManager.createSpace();
|
|
168
125
|
await space1.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
|
|
169
126
|
|
|
170
127
|
// Admit peer2 to space1.
|
|
171
128
|
await writeMessages(
|
|
172
129
|
space1.inner.controlPipeline.writer,
|
|
173
130
|
await createAdmissionCredentials(
|
|
174
|
-
|
|
175
|
-
|
|
131
|
+
peer1.identity.credentialSigner,
|
|
132
|
+
peer2.identity.identityKey,
|
|
176
133
|
space1.key,
|
|
177
134
|
space1.inner.genesisFeedKey,
|
|
178
135
|
),
|
|
179
136
|
);
|
|
180
137
|
|
|
181
138
|
// Accept must be called after admission so that the peer can authenticate for notarization.
|
|
182
|
-
const space2 = await
|
|
139
|
+
const space2 = await peer2.dataSpaceManager.acceptSpace({
|
|
183
140
|
spaceKey: space1.key,
|
|
184
141
|
genesisFeedKey: space1.inner.genesisFeedKey,
|
|
185
142
|
});
|
|
186
143
|
|
|
187
144
|
// Coincidentally, this also waits until a P2P connection is established between peers.
|
|
188
145
|
// TODO(dmaretskyi): Refine this to wait for connection specifically.
|
|
189
|
-
await
|
|
146
|
+
await peer2.dataSpaceManager.waitUntilSpaceReady(space2.key);
|
|
190
147
|
|
|
191
148
|
const [receivedMessage, inc] = latch({ count: 1 });
|
|
192
149
|
space2.listen('test', (message) => {
|
|
@@ -206,18 +163,9 @@ describe('DataSpaceManager', () => {
|
|
|
206
163
|
const peer = builder.createPeer({
|
|
207
164
|
storageType: typeof window === 'undefined' ? StorageType.NODE : StorageType.WEBFS,
|
|
208
165
|
});
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
peer.metadataStore,
|
|
213
|
-
new DataServiceSubscriptions(),
|
|
214
|
-
peer.keyring,
|
|
215
|
-
identity,
|
|
216
|
-
peer.feedStore,
|
|
217
|
-
);
|
|
218
|
-
await dataSpaceManager.open();
|
|
219
|
-
afterTest(() => dataSpaceManager.close());
|
|
220
|
-
const space = await dataSpaceManager.createSpace();
|
|
166
|
+
await peer.createIdentity();
|
|
167
|
+
await openAndClose(peer.dataSpaceManager);
|
|
168
|
+
const space = await peer.dataSpaceManager.createSpace();
|
|
221
169
|
await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
|
|
222
170
|
|
|
223
171
|
const feedDataPath = path.join(space.inner.dataPipeline.pipelineState!.feeds[0].key.toHex(), 'data');
|
|
@@ -241,24 +189,14 @@ describe('DataSpaceManager', () => {
|
|
|
241
189
|
afterTest(async () => builder.destroy());
|
|
242
190
|
|
|
243
191
|
const peer = builder.createPeer();
|
|
244
|
-
|
|
192
|
+
await peer.createIdentity();
|
|
245
193
|
const epochsNumber = 10;
|
|
246
|
-
const dataService = new DataServiceSubscriptions();
|
|
247
194
|
let spaceKey: PublicKey;
|
|
248
195
|
{
|
|
249
196
|
// Create space and create epochs in it.s
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
dataService,
|
|
254
|
-
peer.keyring,
|
|
255
|
-
identity,
|
|
256
|
-
peer.feedStore,
|
|
257
|
-
);
|
|
258
|
-
await dataSpaceManager.open();
|
|
259
|
-
afterTest(() => dataSpaceManager.close());
|
|
260
|
-
|
|
261
|
-
const space = await dataSpaceManager.createSpace();
|
|
197
|
+
await openAndClose(peer.dataSpaceManager);
|
|
198
|
+
|
|
199
|
+
const space = await peer.dataSpaceManager.createSpace();
|
|
262
200
|
spaceKey = space.key;
|
|
263
201
|
await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
|
|
264
202
|
|
|
@@ -267,22 +205,14 @@ describe('DataSpaceManager', () => {
|
|
|
267
205
|
await space.createEpoch();
|
|
268
206
|
}
|
|
269
207
|
await space.close();
|
|
270
|
-
await dataSpaceManager.close();
|
|
208
|
+
await peer.dataSpaceManager.close();
|
|
209
|
+
peer.props.dataSpaceManager = undefined;
|
|
271
210
|
}
|
|
272
211
|
{
|
|
273
212
|
// Load same space and check if it loads only last epoch.s
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
dataService,
|
|
278
|
-
peer.keyring,
|
|
279
|
-
identity,
|
|
280
|
-
peer.feedStore,
|
|
281
|
-
);
|
|
282
|
-
await dataSpaceManager.open();
|
|
283
|
-
afterTest(() => dataSpaceManager.close());
|
|
284
|
-
|
|
285
|
-
const space = dataSpaceManager.spaces.get(spaceKey)!;
|
|
213
|
+
await openAndClose(peer.dataSpaceManager);
|
|
214
|
+
|
|
215
|
+
const space = peer.dataSpaceManager.spaces.get(spaceKey)!;
|
|
286
216
|
|
|
287
217
|
const epochs: number[] = [];
|
|
288
218
|
space.dataPipeline.onNewEpoch.on((epoch: SpecificCredential<Epoch>) => {
|
|
@@ -297,4 +227,28 @@ describe('DataSpaceManager', () => {
|
|
|
297
227
|
}
|
|
298
228
|
});
|
|
299
229
|
});
|
|
230
|
+
|
|
231
|
+
describe('activation', () => {
|
|
232
|
+
test('can activate and deactivate a space', async () => {
|
|
233
|
+
const builder = new TestBuilder();
|
|
234
|
+
|
|
235
|
+
const peer = builder.createPeer();
|
|
236
|
+
await peer.createIdentity();
|
|
237
|
+
await openAndClose(peer.dataSpaceManager);
|
|
238
|
+
|
|
239
|
+
const space = await peer.dataSpaceManager.createSpace();
|
|
240
|
+
await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
|
|
241
|
+
expect(space.state).to.equal(SpaceState.READY);
|
|
242
|
+
|
|
243
|
+
await space.deactivate();
|
|
244
|
+
expect(space.state).to.equal(SpaceState.INACTIVE);
|
|
245
|
+
|
|
246
|
+
await space.activate();
|
|
247
|
+
await asyncTimeout(
|
|
248
|
+
space.stateUpdate.waitForCondition(() => space.state === SpaceState.READY),
|
|
249
|
+
500,
|
|
250
|
+
);
|
|
251
|
+
await testLocalDatabase(space.dataPipeline);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
300
254
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import invariant from 'tiny-invariant';
|
|
6
6
|
|
|
7
7
|
import { Event, synchronized, trackLeaks } from '@dxos/async';
|
|
8
8
|
import { cancelWithContext, Context } from '@dxos/context';
|
|
@@ -86,7 +86,9 @@ export class DataSpaceManager {
|
|
|
86
86
|
try {
|
|
87
87
|
log('load space', { spaceMetadata });
|
|
88
88
|
const space = await this._constructSpace(spaceMetadata);
|
|
89
|
-
|
|
89
|
+
if (spaceMetadata.state !== SpaceState.INACTIVE) {
|
|
90
|
+
space.initializeDataPipelineAsync();
|
|
91
|
+
}
|
|
90
92
|
} catch (err) {
|
|
91
93
|
log.error('Error loading space', { spaceMetadata, err });
|
|
92
94
|
}
|
|
@@ -112,7 +114,7 @@ export class DataSpaceManager {
|
|
|
112
114
|
*/
|
|
113
115
|
@synchronized
|
|
114
116
|
async createSpace() {
|
|
115
|
-
|
|
117
|
+
invariant(this._isOpen, 'Not open.');
|
|
116
118
|
const spaceKey = await this._keyring.createKey();
|
|
117
119
|
const controlFeedKey = await this._keyring.createKey();
|
|
118
120
|
const dataFeedKey = await this._keyring.createKey();
|
|
@@ -121,6 +123,7 @@ export class DataSpaceManager {
|
|
|
121
123
|
genesisFeedKey: controlFeedKey,
|
|
122
124
|
controlFeedKey,
|
|
123
125
|
dataFeedKey,
|
|
126
|
+
state: SpaceState.ACTIVE,
|
|
124
127
|
};
|
|
125
128
|
|
|
126
129
|
log('creating space...', { spaceKey });
|
|
@@ -130,7 +133,7 @@ export class DataSpaceManager {
|
|
|
130
133
|
await this._metadataStore.addSpace(metadata);
|
|
131
134
|
|
|
132
135
|
const memberCredential = credentials[1];
|
|
133
|
-
|
|
136
|
+
invariant(getCredentialAssertion(memberCredential)['@type'] === 'dxos.halo.credentials.SpaceMember');
|
|
134
137
|
await this._signingContext.recordCredential(memberCredential);
|
|
135
138
|
|
|
136
139
|
await space.initializeDataPipeline();
|
|
@@ -143,8 +146,8 @@ export class DataSpaceManager {
|
|
|
143
146
|
@synchronized
|
|
144
147
|
async acceptSpace(opts: AcceptSpaceOptions): Promise<DataSpace> {
|
|
145
148
|
log('accept space', { opts });
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
invariant(this._isOpen, 'Not open.');
|
|
150
|
+
invariant(!this._spaces.has(opts.spaceKey), 'Space already exists.');
|
|
148
151
|
|
|
149
152
|
const metadata: SpaceMetadata = {
|
|
150
153
|
key: opts.spaceKey,
|
|
@@ -155,7 +158,6 @@ export class DataSpaceManager {
|
|
|
155
158
|
|
|
156
159
|
const space = await this._constructSpace(metadata);
|
|
157
160
|
await this._metadataStore.addSpace(metadata);
|
|
158
|
-
|
|
159
161
|
space.initializeDataPipelineAsync();
|
|
160
162
|
|
|
161
163
|
this.updated.emit();
|
|
@@ -218,6 +220,7 @@ export class DataSpaceManager {
|
|
|
218
220
|
|
|
219
221
|
const dataSpace = new DataSpace({
|
|
220
222
|
inner: space,
|
|
223
|
+
initialState: metadata.state === SpaceState.INACTIVE ? SpaceState.INACTIVE : SpaceState.CLOSED,
|
|
221
224
|
metadataStore: this._metadataStore,
|
|
222
225
|
gossip,
|
|
223
226
|
presence,
|
|
@@ -227,7 +230,7 @@ export class DataSpaceManager {
|
|
|
227
230
|
callbacks: {
|
|
228
231
|
beforeReady: async () => {
|
|
229
232
|
log('before space ready', { space: space.key });
|
|
230
|
-
this._dataServiceSubscriptions.registerSpace(
|
|
233
|
+
await this._dataServiceSubscriptions.registerSpace(
|
|
231
234
|
space.key,
|
|
232
235
|
dataSpace.dataPipeline.databaseHost!.createDataServiceHost(),
|
|
233
236
|
);
|
|
@@ -238,11 +241,17 @@ export class DataSpaceManager {
|
|
|
238
241
|
this.updated.emit();
|
|
239
242
|
}
|
|
240
243
|
},
|
|
244
|
+
beforeClose: async () => {
|
|
245
|
+
log('before space close', { space: space.key });
|
|
246
|
+
await this._dataServiceSubscriptions.unregisterSpace(space.key);
|
|
247
|
+
},
|
|
241
248
|
},
|
|
242
249
|
cache: metadata.cache,
|
|
243
250
|
});
|
|
244
251
|
|
|
245
|
-
|
|
252
|
+
if (metadata.state !== SpaceState.INACTIVE) {
|
|
253
|
+
await dataSpace.open();
|
|
254
|
+
}
|
|
246
255
|
|
|
247
256
|
if (metadata.controlTimeframe) {
|
|
248
257
|
dataSpace.inner.controlPipeline.state.setTargetTimeframe(metadata.controlTimeframe);
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Event, scheduleTask, trackLeaks } from '@dxos/async';
|
|
5
|
+
import { Event, scheduleTask, synchronized, trackLeaks } from '@dxos/async';
|
|
6
6
|
import { AUTH_TIMEOUT } from '@dxos/client-protocol';
|
|
7
7
|
import { cancelWithContext, Context } from '@dxos/context';
|
|
8
|
-
import { CredentialConsumer } from '@dxos/credentials';
|
|
9
8
|
import { timed } from '@dxos/debug';
|
|
10
9
|
import { MetadataStore, Space, createMappedFeedWriter, DataPipeline } from '@dxos/echo-pipeline';
|
|
11
10
|
import { CancelledError, SystemError } from '@dxos/errors';
|
|
@@ -36,9 +35,15 @@ export type DataSpaceCallbacks = {
|
|
|
36
35
|
* Called after transitioning to the ready state.
|
|
37
36
|
*/
|
|
38
37
|
afterReady?: () => Promise<void>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Called before space gets closed.
|
|
41
|
+
*/
|
|
42
|
+
beforeClose?: () => Promise<void>;
|
|
39
43
|
};
|
|
40
44
|
|
|
41
45
|
export type DataSpaceParams = {
|
|
46
|
+
initialState: SpaceState;
|
|
42
47
|
inner: Space;
|
|
43
48
|
metadataStore: MetadataStore;
|
|
44
49
|
gossip: Gossip;
|
|
@@ -52,7 +57,7 @@ export type DataSpaceParams = {
|
|
|
52
57
|
|
|
53
58
|
@trackLeaks('open', 'close')
|
|
54
59
|
export class DataSpace {
|
|
55
|
-
private
|
|
60
|
+
private _ctx = new Context();
|
|
56
61
|
private readonly _inner: Space;
|
|
57
62
|
private readonly _gossip: Gossip;
|
|
58
63
|
private readonly _presence: Presence;
|
|
@@ -60,7 +65,7 @@ export class DataSpace {
|
|
|
60
65
|
private readonly _feedStore: FeedStore<FeedMessage>;
|
|
61
66
|
private readonly _metadataStore: MetadataStore;
|
|
62
67
|
private readonly _signingContext: SigningContext;
|
|
63
|
-
private readonly
|
|
68
|
+
private readonly _notarizationPlugin = new NotarizationPlugin();
|
|
64
69
|
private readonly _callbacks: DataSpaceCallbacks;
|
|
65
70
|
private readonly _cache?: SpaceCache;
|
|
66
71
|
|
|
@@ -94,8 +99,9 @@ export class DataSpace {
|
|
|
94
99
|
authTimeout: AUTH_TIMEOUT,
|
|
95
100
|
});
|
|
96
101
|
|
|
97
|
-
this._notarizationPluginConsumer = this._inner.spaceState.registerProcessor(new NotarizationPlugin());
|
|
98
102
|
this._cache = params.cache;
|
|
103
|
+
|
|
104
|
+
this._state = params.initialState;
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
get key() {
|
|
@@ -124,30 +130,44 @@ export class DataSpace {
|
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
get notarizationPlugin() {
|
|
127
|
-
return this.
|
|
133
|
+
return this._notarizationPlugin;
|
|
128
134
|
}
|
|
129
135
|
|
|
130
136
|
get cache() {
|
|
131
137
|
return this._cache;
|
|
132
138
|
}
|
|
133
139
|
|
|
140
|
+
@synchronized
|
|
134
141
|
async open() {
|
|
135
|
-
await this.
|
|
136
|
-
|
|
142
|
+
await this._open();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private async _open() {
|
|
146
|
+
await this._notarizationPlugin.open();
|
|
147
|
+
await this._inner.spaceState.addCredentialProcessor(this._notarizationPlugin);
|
|
137
148
|
await this._inner.open();
|
|
138
|
-
this._state = SpaceState.
|
|
149
|
+
this._state = SpaceState.CONTROL_ONLY;
|
|
150
|
+
this.stateUpdate.emit();
|
|
151
|
+
this.metrics = {};
|
|
139
152
|
this.metrics.open = new Date();
|
|
140
153
|
}
|
|
141
154
|
|
|
155
|
+
@synchronized
|
|
142
156
|
async close() {
|
|
157
|
+
await this._close();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private async _close() {
|
|
161
|
+
await this._callbacks.beforeClose?.();
|
|
143
162
|
this._state = SpaceState.CLOSED;
|
|
144
163
|
await this._ctx.dispose();
|
|
164
|
+
this._ctx = new Context();
|
|
145
165
|
|
|
146
166
|
await this.authVerifier.close();
|
|
147
167
|
|
|
148
168
|
await this._inner.close();
|
|
149
|
-
await this.
|
|
150
|
-
await this.
|
|
169
|
+
await this._inner.spaceState.removeCredentialProcessor(this._notarizationPlugin);
|
|
170
|
+
await this._notarizationPlugin.close();
|
|
151
171
|
|
|
152
172
|
await this._presence.destroy();
|
|
153
173
|
}
|
|
@@ -185,7 +205,7 @@ export class DataSpace {
|
|
|
185
205
|
}
|
|
186
206
|
|
|
187
207
|
async initializeDataPipeline() {
|
|
188
|
-
if (this._state !== SpaceState.
|
|
208
|
+
if (this._state !== SpaceState.CONTROL_ONLY) {
|
|
189
209
|
throw new SystemError('Invalid operation');
|
|
190
210
|
}
|
|
191
211
|
this._state = SpaceState.INITIALIZING;
|
|
@@ -201,14 +221,16 @@ export class DataSpace {
|
|
|
201
221
|
log('Writable feeds created');
|
|
202
222
|
this.stateUpdate.emit();
|
|
203
223
|
|
|
204
|
-
this.notarizationPlugin.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
credential
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
224
|
+
if (!this.notarizationPlugin.hasWriter) {
|
|
225
|
+
this.notarizationPlugin.setWriter(
|
|
226
|
+
createMappedFeedWriter<Credential, FeedMessage.Payload>(
|
|
227
|
+
(credential) => ({
|
|
228
|
+
credential: { credential },
|
|
229
|
+
}),
|
|
230
|
+
this._inner.controlPipeline.writer,
|
|
231
|
+
),
|
|
232
|
+
);
|
|
233
|
+
}
|
|
212
234
|
|
|
213
235
|
await this._inner.initializeDataPipeline();
|
|
214
236
|
|
|
@@ -309,4 +331,28 @@ export class DataSpace {
|
|
|
309
331
|
}
|
|
310
332
|
}
|
|
311
333
|
}
|
|
334
|
+
|
|
335
|
+
@synchronized
|
|
336
|
+
async activate() {
|
|
337
|
+
if (this._state !== SpaceState.INACTIVE) {
|
|
338
|
+
throw new SystemError('Invalid operation');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
await this._metadataStore.setSpaceState(this.key, SpaceState.ACTIVE);
|
|
342
|
+
await this._open();
|
|
343
|
+
this.initializeDataPipelineAsync();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
@synchronized
|
|
347
|
+
async deactivate() {
|
|
348
|
+
if (this._state === SpaceState.INACTIVE) {
|
|
349
|
+
throw new SystemError('Invalid operation');
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// de-register from data service
|
|
353
|
+
await this._metadataStore.setSpaceState(this.key, SpaceState.INACTIVE);
|
|
354
|
+
await this._close();
|
|
355
|
+
this._state = SpaceState.INACTIVE;
|
|
356
|
+
this.stateUpdate.emit();
|
|
357
|
+
}
|
|
312
358
|
}
|
|
@@ -25,7 +25,7 @@ class TestAgent extends TestPeer {
|
|
|
25
25
|
super();
|
|
26
26
|
this.feed.written.on(this._ctx, async ([credential]) => {
|
|
27
27
|
log('written to feed', { credential });
|
|
28
|
-
await this.notarizationPlugin.
|
|
28
|
+
await this.notarizationPlugin.processCredential(credential);
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import invariant from 'tiny-invariant';
|
|
6
6
|
|
|
7
7
|
import { DeferredTask, Event, scheduleTask, sleep, TimeoutError, Trigger } from '@dxos/async';
|
|
8
8
|
import { Context, rejectOnDispose } from '@dxos/context';
|
|
@@ -68,6 +68,10 @@ export class NotarizationPlugin implements CredentialProcessor {
|
|
|
68
68
|
private readonly _processedCredentials = new ComplexSet<PublicKey>(PublicKey.hash);
|
|
69
69
|
private readonly _processCredentialsTriggers = new ComplexMap<PublicKey, Trigger>(PublicKey.hash);
|
|
70
70
|
|
|
71
|
+
get hasWriter() {
|
|
72
|
+
return !!this._writer;
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
async open() {}
|
|
72
76
|
|
|
73
77
|
async close() {
|
|
@@ -85,7 +89,7 @@ export class NotarizationPlugin implements CredentialProcessor {
|
|
|
85
89
|
successDelay = DEFAULT_SUCCESS_DELAY,
|
|
86
90
|
}: NotarizeParams) {
|
|
87
91
|
log('notarize', { credentials });
|
|
88
|
-
|
|
92
|
+
invariant(
|
|
89
93
|
credentials.every((credential) => credential.id),
|
|
90
94
|
'Credentials must have an id',
|
|
91
95
|
);
|
|
@@ -165,7 +169,7 @@ export class NotarizationPlugin implements CredentialProcessor {
|
|
|
165
169
|
/**
|
|
166
170
|
* Called with credentials arriving from the control pipeline.
|
|
167
171
|
*/
|
|
168
|
-
async
|
|
172
|
+
async processCredential(credential: Credential) {
|
|
169
173
|
if (!credential.id) {
|
|
170
174
|
return;
|
|
171
175
|
}
|
|
@@ -175,7 +179,7 @@ export class NotarizationPlugin implements CredentialProcessor {
|
|
|
175
179
|
}
|
|
176
180
|
|
|
177
181
|
setWriter(writer: FeedWriter<Credential>) {
|
|
178
|
-
|
|
182
|
+
invariant(!this._writer, 'Writer already set.');
|
|
179
183
|
this._writer = writer;
|
|
180
184
|
}
|
|
181
185
|
|
|
@@ -194,7 +198,7 @@ export class NotarizationPlugin implements CredentialProcessor {
|
|
|
194
198
|
throw new Error(WRITER_NOT_SET_ERROR_CODE);
|
|
195
199
|
}
|
|
196
200
|
for (const credential of request.credentials ?? []) {
|
|
197
|
-
|
|
201
|
+
invariant(credential.id, 'Credential must have an id');
|
|
198
202
|
if (this._processedCredentials.has(credential.id)) {
|
|
199
203
|
continue;
|
|
200
204
|
}
|