@dxos/client-services 0.1.23 → 0.1.24-next.6b1f434
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-ZPZBLOQW.mjs +4086 -0
- package/dist/lib/browser/chunk-ZPZBLOQW.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +66 -3066
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +198 -0
- package/dist/lib/browser/packlets/testing/index.mjs.map +7 -0
- package/dist/lib/node/index.cjs +1655 -669
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +3806 -0
- package/dist/lib/node/packlets/testing/index.cjs.map +7 -0
- package/dist/types/src/packlets/deprecated/profile.d.ts +3 -1
- package/dist/types/src/packlets/deprecated/profile.d.ts.map +1 -1
- package/dist/types/src/packlets/deprecated/space.d.ts.map +1 -1
- package/dist/types/src/packlets/deprecated/system.d.ts +15 -9
- package/dist/types/src/packlets/deprecated/system.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/devtools.d.ts +2 -2
- package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/feeds.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/network.d.ts +3 -3
- package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/spaces.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.d.ts +2 -2
- package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts +8 -2
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +3 -2
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/halo-invitations-handler.d.ts +11 -3
- package/dist/types/src/packlets/invitations/halo-invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations.d.ts +1 -1
- package/dist/types/src/packlets/invitations/invitations.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts +1 -1
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +3 -1
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.test.d.ts +2 -0
- package/dist/types/src/packlets/services/service-context.test.d.ts.map +1 -0
- package/dist/types/src/packlets/services/service-definitions.d.ts +1 -1
- package/dist/types/src/packlets/services/service-definitions.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +9 -6
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-registry.d.ts +4 -3
- package/dist/types/src/packlets/services/service-registry.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +9 -2
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +18 -4
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +50 -0
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/notarization-plugin.test.d.ts +2 -0
- package/dist/types/src/packlets/spaces/notarization-plugin.test.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +3 -2
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/credential-utils.d.ts +8 -0
- package/dist/types/src/packlets/testing/credential-utils.d.ts.map +1 -0
- package/dist/types/src/packlets/testing/index.d.ts +2 -0
- package/dist/types/src/packlets/testing/index.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts +3 -0
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -0
- package/dist/types/src/packlets/testing/test-builder.d.ts +2 -2
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts +35 -0
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/{iframe-runtime.d.ts → iframe-proxy-runtime.d.ts} +9 -4
- package/dist/types/src/packlets/vault/iframe-proxy-runtime.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/index.d.ts +6 -1
- package/dist/types/src/packlets/vault/index.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/services.d.ts +9 -1
- package/dist/types/src/packlets/vault/services.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/shell-controller.d.ts +24 -0
- package/dist/types/src/packlets/vault/shell-controller.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/shell-runtime.d.ts +45 -0
- package/dist/types/src/packlets/vault/shell-runtime.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/vault-resource-lock.d.ts +19 -0
- package/dist/types/src/packlets/vault/vault-resource-lock.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/worker-runtime.d.ts +2 -1
- package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-session.d.ts +7 -4
- package/dist/types/src/packlets/vault/worker-session.d.ts.map +1 -1
- package/package.json +30 -29
- package/src/packlets/deprecated/profile.ts +20 -2
- package/src/packlets/deprecated/space.ts +41 -37
- package/src/packlets/deprecated/system.ts +26 -13
- package/src/packlets/devtools/devtools.ts +4 -4
- package/src/packlets/devtools/feeds.ts +11 -6
- package/src/packlets/devtools/network.ts +20 -9
- package/src/packlets/devtools/spaces.ts +8 -7
- package/src/packlets/identity/authenticator.ts +12 -7
- package/src/packlets/identity/identity-manager.test.ts +25 -16
- package/src/packlets/identity/identity-manager.ts +23 -9
- package/src/packlets/identity/identity.test.ts +22 -23
- package/src/packlets/identity/identity.ts +11 -6
- package/src/packlets/invitations/halo-invitations-handler.test.ts +4 -45
- package/src/packlets/invitations/halo-invitations-handler.ts +33 -17
- package/src/packlets/invitations/invitations.ts +2 -1
- package/src/packlets/invitations/space-invitations-handler.test.ts +43 -26
- package/src/packlets/invitations/space-invitations-handler.ts +9 -12
- package/src/packlets/services/service-context.test.ts +56 -0
- package/src/packlets/services/service-context.ts +62 -6
- package/src/packlets/services/service-definitions.ts +1 -1
- package/src/packlets/services/service-host.test.ts +74 -5
- package/src/packlets/services/service-host.ts +86 -24
- package/src/packlets/services/service-registry.test.ts +2 -1
- package/src/packlets/services/service-registry.ts +5 -1
- package/src/packlets/spaces/data-space-manager.test.ts +72 -15
- package/src/packlets/spaces/data-space-manager.ts +53 -12
- package/src/packlets/spaces/data-space.ts +113 -14
- package/src/packlets/spaces/notarization-plugin.test.ts +81 -0
- package/src/packlets/spaces/notarization-plugin.ts +211 -0
- package/src/packlets/spaces/spaces-service.ts +10 -2
- package/src/packlets/testing/credential-utils.ts +28 -0
- package/src/packlets/testing/index.ts +2 -0
- package/src/packlets/testing/invitation-utils.ts +44 -0
- package/src/packlets/testing/test-builder.ts +14 -18
- package/src/packlets/vault/iframe-host-runtime.ts +131 -0
- package/src/packlets/vault/{iframe-runtime.ts → iframe-proxy-runtime.ts} +18 -3
- package/src/packlets/vault/index.ts +6 -1
- package/src/packlets/vault/services.ts +17 -1
- package/src/packlets/vault/shell-controller.ts +77 -0
- package/src/packlets/vault/shell-runtime.ts +133 -0
- package/src/packlets/vault/vault-resource-lock.ts +76 -0
- package/src/packlets/vault/worker-runtime.ts +16 -4
- package/src/packlets/vault/worker-session.ts +36 -26
- package/dist/types/src/packlets/vault/iframe-runtime.d.ts.map +0 -1
|
@@ -3,31 +3,32 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Trigger } from '@dxos/async';
|
|
6
|
+
import { CredentialConsumer, getCredentialAssertion } from '@dxos/credentials';
|
|
6
7
|
import { failUndefined } from '@dxos/debug';
|
|
7
8
|
import {
|
|
8
9
|
valueEncoding,
|
|
9
|
-
DataServiceSubscriptions,
|
|
10
10
|
MetadataStore,
|
|
11
11
|
SpaceManager,
|
|
12
12
|
SigningContext,
|
|
13
|
+
DataServiceSubscriptions,
|
|
13
14
|
SnapshotStore
|
|
14
|
-
} from '@dxos/echo-
|
|
15
|
+
} from '@dxos/echo-pipeline';
|
|
15
16
|
import { FeedFactory, FeedStore } from '@dxos/feed-store';
|
|
16
17
|
import { Keyring } from '@dxos/keyring';
|
|
17
18
|
import { log } from '@dxos/log';
|
|
18
19
|
import { ModelFactory } from '@dxos/model-factory';
|
|
19
20
|
import { NetworkManager } from '@dxos/network-manager';
|
|
20
21
|
import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
22
|
+
import { Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
21
23
|
import { Storage } from '@dxos/random-access-storage';
|
|
22
24
|
|
|
23
|
-
import { CreateIdentityOptions, IdentityManager } from '../identity';
|
|
25
|
+
import { CreateIdentityOptions, IdentityManager, JoinIdentityParams } from '../identity';
|
|
24
26
|
import { HaloInvitationsHandler, SpaceInvitationsHandler } from '../invitations';
|
|
25
27
|
import { DataSpaceManager } from '../spaces/data-space-manager';
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
30
|
* Shared backend for all client services.
|
|
29
31
|
*/
|
|
30
|
-
|
|
31
32
|
// TODO(burdon): Rename/break-up into smaller components. And/or make members private.
|
|
32
33
|
export class ServiceContext {
|
|
33
34
|
public readonly initialized = new Trigger();
|
|
@@ -40,6 +41,8 @@ export class ServiceContext {
|
|
|
40
41
|
public readonly identityManager: IdentityManager;
|
|
41
42
|
public readonly haloInvitations: HaloInvitationsHandler;
|
|
42
43
|
|
|
44
|
+
private _deviceSpaceSync?: CredentialConsumer<any>;
|
|
45
|
+
|
|
43
46
|
// Initialized after identity is initialized.
|
|
44
47
|
public dataSpaceManager?: DataSpaceManager;
|
|
45
48
|
public spaceInvitations?: SpaceInvitationsHandler;
|
|
@@ -64,19 +67,27 @@ export class ServiceContext {
|
|
|
64
67
|
}
|
|
65
68
|
})
|
|
66
69
|
});
|
|
70
|
+
|
|
67
71
|
this.spaceManager = new SpaceManager({
|
|
68
72
|
feedStore: this.feedStore,
|
|
69
73
|
networkManager: this.networkManager
|
|
70
74
|
});
|
|
75
|
+
|
|
71
76
|
this.identityManager = new IdentityManager(
|
|
72
77
|
this.metadataStore,
|
|
73
78
|
this.keyring,
|
|
79
|
+
this.feedStore,
|
|
74
80
|
this.spaceManager
|
|
75
81
|
);
|
|
76
82
|
|
|
77
83
|
// TODO(burdon): _initialize called in multiple places.
|
|
78
84
|
// TODO(burdon): Call _initialize on success.
|
|
79
|
-
this.haloInvitations = new HaloInvitationsHandler(
|
|
85
|
+
this.haloInvitations = new HaloInvitationsHandler({
|
|
86
|
+
networkManager: this.networkManager,
|
|
87
|
+
getIdentity: () => this.identityManager.identity ?? failUndefined(),
|
|
88
|
+
acceptIdentity: this._acceptIdentity.bind(this),
|
|
89
|
+
keyring: this.keyring
|
|
90
|
+
});
|
|
80
91
|
}
|
|
81
92
|
|
|
82
93
|
async open() {
|
|
@@ -92,6 +103,7 @@ export class ServiceContext {
|
|
|
92
103
|
|
|
93
104
|
async close() {
|
|
94
105
|
log('closing...');
|
|
106
|
+
await this._deviceSpaceSync?.close();
|
|
95
107
|
await this.dataSpaceManager?.close();
|
|
96
108
|
await this.identityManager.close();
|
|
97
109
|
await this.spaceManager.close();
|
|
@@ -115,6 +127,13 @@ export class ServiceContext {
|
|
|
115
127
|
return identity;
|
|
116
128
|
}
|
|
117
129
|
|
|
130
|
+
private async _acceptIdentity(params: JoinIdentityParams) {
|
|
131
|
+
const identity = await this.identityManager.acceptIdentity(params);
|
|
132
|
+
|
|
133
|
+
await this._initialize();
|
|
134
|
+
return identity;
|
|
135
|
+
}
|
|
136
|
+
|
|
118
137
|
// Called when identity is created.
|
|
119
138
|
private async _initialize() {
|
|
120
139
|
log('initializing spaces...');
|
|
@@ -123,7 +142,10 @@ export class ServiceContext {
|
|
|
123
142
|
credentialSigner: identity.getIdentityCredentialSigner(),
|
|
124
143
|
identityKey: identity.identityKey,
|
|
125
144
|
deviceKey: identity.deviceKey,
|
|
126
|
-
profile: identity.profileDocument
|
|
145
|
+
profile: identity.profileDocument,
|
|
146
|
+
recordCredential: async (credential) => {
|
|
147
|
+
await identity.controlPipeline.writer.write({ credential: { credential } });
|
|
148
|
+
}
|
|
127
149
|
};
|
|
128
150
|
|
|
129
151
|
this.dataSpaceManager = new DataSpaceManager(
|
|
@@ -133,9 +155,11 @@ export class ServiceContext {
|
|
|
133
155
|
this.keyring,
|
|
134
156
|
signingContext,
|
|
135
157
|
this.modelFactory,
|
|
158
|
+
this.feedStore,
|
|
136
159
|
this.snapshotStore
|
|
137
160
|
);
|
|
138
161
|
await this.dataSpaceManager.open();
|
|
162
|
+
|
|
139
163
|
this.spaceInvitations = new SpaceInvitationsHandler(
|
|
140
164
|
this.networkManager,
|
|
141
165
|
this.dataSpaceManager,
|
|
@@ -143,5 +167,37 @@ export class ServiceContext {
|
|
|
143
167
|
this.keyring
|
|
144
168
|
);
|
|
145
169
|
this.initialized.wake();
|
|
170
|
+
|
|
171
|
+
this._deviceSpaceSync = identity.space.spaceState.registerProcessor({
|
|
172
|
+
process: async (credential: Credential) => {
|
|
173
|
+
const assertion = getCredentialAssertion(credential);
|
|
174
|
+
if (assertion['@type'] !== 'dxos.halo.credentials.SpaceMember') {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (assertion.spaceKey.equals(identity.space.key)) {
|
|
178
|
+
// ignore halo space
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (!this.dataSpaceManager) {
|
|
182
|
+
log('dataSpaceManager not initialized yet, ignoring space admission', { details: assertion });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (this.dataSpaceManager.spaces.has(assertion.spaceKey)) {
|
|
186
|
+
log('space already exists, ignoring space admission', { details: assertion });
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
log('accepting space recorded in halo', { details: assertion });
|
|
192
|
+
await this.dataSpaceManager.acceptSpace({
|
|
193
|
+
spaceKey: assertion.spaceKey,
|
|
194
|
+
genesisFeedKey: assertion.genesisFeedKey
|
|
195
|
+
});
|
|
196
|
+
} catch (err) {
|
|
197
|
+
log.catch(err);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
await this._deviceSpaceSync.open();
|
|
146
202
|
}
|
|
147
203
|
}
|
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
|
|
7
|
+
import { latch, Trigger } from '@dxos/async';
|
|
6
8
|
import { Config } from '@dxos/config';
|
|
9
|
+
import { verifyPresentation } from '@dxos/credentials';
|
|
10
|
+
import { PublicKey } from '@dxos/keys';
|
|
7
11
|
import { MemorySignalManagerContext } from '@dxos/messaging';
|
|
12
|
+
import { Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
8
13
|
import { afterTest, describe, test } from '@dxos/test';
|
|
9
14
|
|
|
10
|
-
import { createServiceHost } from '../testing';
|
|
15
|
+
import { createMockCredential, createServiceHost } from '../testing';
|
|
11
16
|
|
|
12
17
|
describe('ClientServicesHost', () => {
|
|
13
18
|
test('queryCredentials', async () => {
|
|
@@ -15,10 +20,10 @@ describe('ClientServicesHost', () => {
|
|
|
15
20
|
await host.open();
|
|
16
21
|
afterTest(() => host.close());
|
|
17
22
|
|
|
18
|
-
await host.services.ProfileService
|
|
19
|
-
const { publicKey: spaceKey } = await host.services.SpaceService
|
|
23
|
+
await host.services.ProfileService!.createProfile({});
|
|
24
|
+
const { publicKey: spaceKey } = await host.services.SpaceService!.createSpace();
|
|
20
25
|
|
|
21
|
-
const stream = host.services.SpacesService
|
|
26
|
+
const stream = host.services.SpacesService!.queryCredentials({ spaceKey });
|
|
22
27
|
const [done, tick] = latch({ count: 3 });
|
|
23
28
|
stream.subscribe((credential) => {
|
|
24
29
|
tick();
|
|
@@ -28,4 +33,68 @@ describe('ClientServicesHost', () => {
|
|
|
28
33
|
|
|
29
34
|
await done();
|
|
30
35
|
});
|
|
36
|
+
|
|
37
|
+
test('write and query credentials', async () => {
|
|
38
|
+
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
39
|
+
await host.open();
|
|
40
|
+
afterTest(() => host.close());
|
|
41
|
+
|
|
42
|
+
await host.services.ProfileService!.createProfile({});
|
|
43
|
+
|
|
44
|
+
const testCredential = await createMockCredential({
|
|
45
|
+
signer: host._serviceContext.keyring,
|
|
46
|
+
issuer: host._serviceContext.identityManager.identity!.deviceKey
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Test if Profile exposes haloSpace key.
|
|
50
|
+
const haloSpace = new Trigger<PublicKey>();
|
|
51
|
+
host.services.ProfileService!.subscribeProfile()!.subscribe(({ profile }) => {
|
|
52
|
+
if (profile?.haloSpace) {
|
|
53
|
+
haloSpace.wake(profile.haloSpace);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
await host.services.SpacesService?.writeCredentials({
|
|
58
|
+
spaceKey: await haloSpace.wait(),
|
|
59
|
+
credentials: [testCredential]
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const credentials = host.services.SpacesService!.queryCredentials({ spaceKey: await haloSpace.wait() });
|
|
63
|
+
const queriedCredential = new Trigger<Credential>();
|
|
64
|
+
credentials.subscribe((credential) => {
|
|
65
|
+
if (credential.subject.id.equals(testCredential.subject.id)) {
|
|
66
|
+
queriedCredential.wake(credential);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
afterTest(() => credentials.close());
|
|
70
|
+
|
|
71
|
+
await queriedCredential.wait();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('sign presentation', async () => {
|
|
75
|
+
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
76
|
+
await host.open();
|
|
77
|
+
afterTest(() => host.close());
|
|
78
|
+
|
|
79
|
+
await host.services.ProfileService!.createProfile({});
|
|
80
|
+
|
|
81
|
+
const testCredential = await createMockCredential({
|
|
82
|
+
signer: host._serviceContext.keyring,
|
|
83
|
+
issuer: host._serviceContext.identityManager.identity!.deviceKey
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const nonce = new Uint8Array([0, 0, 0, 0]);
|
|
87
|
+
|
|
88
|
+
const presentation = await host.services.ProfileService!.signPresentation({
|
|
89
|
+
presentation: {
|
|
90
|
+
credentials: [testCredential]
|
|
91
|
+
},
|
|
92
|
+
nonce
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
expect(presentation.proofs?.[0].nonce).to.deep.equal(nonce);
|
|
96
|
+
expect(await verifyPresentation(presentation)).to.deep.equal({
|
|
97
|
+
kind: 'pass'
|
|
98
|
+
});
|
|
99
|
+
});
|
|
31
100
|
});
|
|
@@ -4,21 +4,23 @@
|
|
|
4
4
|
|
|
5
5
|
import { Config } from '@dxos/config';
|
|
6
6
|
import { raise } from '@dxos/debug';
|
|
7
|
-
import {
|
|
7
|
+
import { DocumentModel } from '@dxos/document-model';
|
|
8
|
+
import { DataServiceImpl } from '@dxos/echo-pipeline';
|
|
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 {
|
|
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,
|
|
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(
|
|
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
|
|
44
|
-
private _serviceRegistry
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const deviceKey = this._serviceContext.identityManager.identity?.deviceKey;
|
|
77
|
-
log('opened', { deviceKey });
|
|
78
|
-
}
|
|
121
|
+
if (this._open) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
79
124
|
|
|
80
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 {
|
|
8
|
+
import { DocumentModel } from '@dxos/document-model';
|
|
9
|
+
import { AuthStatus, DataServiceSubscriptions } from '@dxos/echo-pipeline';
|
|
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(
|
|
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(
|
|
69
|
+
new ModelFactory().registerModel(DocumentModel),
|
|
70
|
+
peer2.feedStore,
|
|
42
71
|
peer1.snapshotStore
|
|
43
72
|
);
|
|
44
73
|
|
|
45
74
|
const space1 = await dataSpaceManager1.createSpace();
|
|
46
|
-
|
|
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
|
-
|
|
62
|
-
space2.inner.dataFeedKey,
|
|
63
|
-
space2.inner.genesisFeedKey
|
|
84
|
+
space1.inner.genesisFeedKey
|
|
64
85
|
)
|
|
65
86
|
);
|
|
66
87
|
|
|
67
|
-
|
|
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);
|