@dxos/client-services 0.1.53-main.52ca4e9 → 0.1.53-main.5bcfb84
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-PWZ2J6KS.mjs → chunk-EYXOVYW5.mjs} +170 -90
- package/dist/lib/browser/chunk-EYXOVYW5.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +19 -2
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +165 -85
- 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 +179 -83
- package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
- 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/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 +1 -1
- 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/package.json +32 -32
- package/src/packlets/identity/identity.ts +21 -26
- package/src/packlets/services/service-context.ts +9 -7
- package/src/packlets/spaces/data-space-manager.test.ts +72 -118
- package/src/packlets/spaces/data-space-manager.ts +13 -3
- 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 +5 -1
- package/src/packlets/spaces/spaces-service.ts +26 -8
- package/src/packlets/testing/test-builder.ts +35 -2
- package/dist/lib/browser/chunk-PWZ2J6KS.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
|
});
|
|
@@ -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
|
}
|
|
@@ -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 });
|
|
@@ -217,6 +220,7 @@ export class DataSpaceManager {
|
|
|
217
220
|
|
|
218
221
|
const dataSpace = new DataSpace({
|
|
219
222
|
inner: space,
|
|
223
|
+
initialState: metadata.state === SpaceState.INACTIVE ? SpaceState.INACTIVE : SpaceState.CLOSED,
|
|
220
224
|
metadataStore: this._metadataStore,
|
|
221
225
|
gossip,
|
|
222
226
|
presence,
|
|
@@ -226,7 +230,7 @@ export class DataSpaceManager {
|
|
|
226
230
|
callbacks: {
|
|
227
231
|
beforeReady: async () => {
|
|
228
232
|
log('before space ready', { space: space.key });
|
|
229
|
-
this._dataServiceSubscriptions.registerSpace(
|
|
233
|
+
await this._dataServiceSubscriptions.registerSpace(
|
|
230
234
|
space.key,
|
|
231
235
|
dataSpace.dataPipeline.databaseHost!.createDataServiceHost(),
|
|
232
236
|
);
|
|
@@ -237,11 +241,17 @@ export class DataSpaceManager {
|
|
|
237
241
|
this.updated.emit();
|
|
238
242
|
}
|
|
239
243
|
},
|
|
244
|
+
beforeClose: async () => {
|
|
245
|
+
log('before space close', { space: space.key });
|
|
246
|
+
await this._dataServiceSubscriptions.unregisterSpace(space.key);
|
|
247
|
+
},
|
|
240
248
|
},
|
|
241
249
|
cache: metadata.cache,
|
|
242
250
|
});
|
|
243
251
|
|
|
244
|
-
|
|
252
|
+
if (metadata.state !== SpaceState.INACTIVE) {
|
|
253
|
+
await dataSpace.open();
|
|
254
|
+
}
|
|
245
255
|
|
|
246
256
|
if (metadata.controlTimeframe) {
|
|
247
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
|
|
|
@@ -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() {
|
|
@@ -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
|
}
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import { EventSubscriptions, UpdateScheduler, scheduleTask } from '@dxos/async';
|
|
6
6
|
import { Stream } from '@dxos/codec-protobuf';
|
|
7
|
-
import {
|
|
7
|
+
import { CredentialProcessor } from '@dxos/credentials';
|
|
8
|
+
import { raise } from '@dxos/debug';
|
|
8
9
|
import { DataServiceSubscriptions, SpaceManager, SpaceNotFoundError } from '@dxos/echo-pipeline';
|
|
10
|
+
import { ApiError } from '@dxos/errors';
|
|
9
11
|
import { log } from '@dxos/log';
|
|
10
12
|
import { encodeError } from '@dxos/protocols';
|
|
11
13
|
import {
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
QuerySpacesResponse,
|
|
16
18
|
Space,
|
|
17
19
|
SpaceMember,
|
|
20
|
+
SpaceState,
|
|
18
21
|
SpacesService,
|
|
19
22
|
SubscribeMessagesRequest,
|
|
20
23
|
UpdateSpaceRequest,
|
|
@@ -46,8 +49,23 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
46
49
|
return this._serializeSpace(space);
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
async updateSpace(
|
|
50
|
-
|
|
52
|
+
async updateSpace({ spaceKey, state }: UpdateSpaceRequest) {
|
|
53
|
+
const dataSpaceManager = await this._getDataSpaceManager();
|
|
54
|
+
const space = dataSpaceManager.spaces.get(spaceKey) ?? raise(new SpaceNotFoundError(spaceKey));
|
|
55
|
+
|
|
56
|
+
if (state) {
|
|
57
|
+
switch (state) {
|
|
58
|
+
case SpaceState.ACTIVE:
|
|
59
|
+
await space.activate();
|
|
60
|
+
break;
|
|
61
|
+
|
|
62
|
+
case SpaceState.INACTIVE:
|
|
63
|
+
await space.deactivate();
|
|
64
|
+
break;
|
|
65
|
+
default:
|
|
66
|
+
throw new ApiError('Invalid space state');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
51
69
|
}
|
|
52
70
|
|
|
53
71
|
querySpaces(): Stream<QuerySpacesResponse> {
|
|
@@ -124,13 +142,13 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
124
142
|
return new Stream(({ ctx, next }) => {
|
|
125
143
|
const space = this._spaceManager.spaces.get(spaceKey) ?? raise(new SpaceNotFoundError(spaceKey));
|
|
126
144
|
|
|
127
|
-
const processor =
|
|
128
|
-
|
|
145
|
+
const processor: CredentialProcessor = {
|
|
146
|
+
processCredential: async (credential) => {
|
|
129
147
|
next(credential);
|
|
130
148
|
},
|
|
131
|
-
}
|
|
132
|
-
ctx.onDispose(() =>
|
|
133
|
-
scheduleTask(ctx, () =>
|
|
149
|
+
};
|
|
150
|
+
ctx.onDispose(() => space.spaceState.removeCredentialProcessor(processor));
|
|
151
|
+
scheduleTask(ctx, () => space.spaceState.addCredentialProcessor(processor));
|
|
134
152
|
});
|
|
135
153
|
}
|
|
136
154
|
|