@dxos/client-services 0.5.9-main.bdf733d → 0.5.9-main.bf0ae3e
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-KNGR7BYM.mjs → chunk-4IR3JP4U.mjs} +767 -605
- package/dist/lib/browser/chunk-4IR3JP4U.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 +11 -11
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-WWHTBQNR.cjs → chunk-ZBIDLLZ4.cjs} +982 -824
- package/dist/lib/node/chunk-ZBIDLLZ4.cjs.map +7 -0
- package/dist/lib/node/index.cjs +42 -42
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +17 -17
- package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
- package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +19 -0
- package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +1 -0
- package/dist/types/src/packlets/identity/identity-service.d.ts +14 -7
- package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +4 -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 +1 -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 +5 -3
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +1 -0
- package/dist/types/src/packlets/spaces/data-space.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 +8 -6
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/package.json +36 -36
- package/src/packlets/identity/default-space-state-machine.ts +44 -0
- package/src/packlets/identity/identity-service.test.ts +35 -5
- package/src/packlets/identity/identity-service.ts +50 -8
- package/src/packlets/identity/identity.ts +25 -2
- package/src/packlets/services/service-context.ts +1 -4
- package/src/packlets/services/service-host.ts +13 -40
- package/src/packlets/spaces/data-space-manager.test.ts +46 -1
- package/src/packlets/spaces/data-space-manager.ts +54 -25
- package/src/packlets/spaces/data-space.ts +24 -5
- package/src/packlets/spaces/spaces-service.ts +2 -2
- package/src/packlets/testing/test-builder.ts +12 -10
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-KNGR7BYM.mjs.map +0 -7
- package/dist/lib/node/chunk-WWHTBQNR.cjs.map +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/client-services",
|
|
3
|
-
"version": "0.5.9-main.
|
|
3
|
+
"version": "0.5.9-main.bf0ae3e",
|
|
4
4
|
"description": "DXOS client services implementation",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -23,45 +23,45 @@
|
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"platform": "^1.3.6",
|
|
26
|
-
"@dxos/async": "0.5.9-main.
|
|
27
|
-
"@dxos/client-protocol": "0.5.9-main.
|
|
28
|
-
"@dxos/codec-protobuf": "0.5.9-main.
|
|
29
|
-
"@dxos/config": "0.5.9-main.
|
|
30
|
-
"@dxos/
|
|
31
|
-
"@dxos/
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/debug": "0.5.9-main.
|
|
34
|
-
"@dxos/crypto": "0.5.9-main.
|
|
35
|
-
"@dxos/echo-
|
|
36
|
-
"@dxos/echo-
|
|
37
|
-
"@dxos/echo-protocol": "0.5.9-main.
|
|
38
|
-
"@dxos/echo-schema": "0.5.9-main.
|
|
39
|
-
"@dxos/feed-store": "0.5.9-main.
|
|
40
|
-
"@dxos/indexing": "0.5.9-main.
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/keys": "0.5.9-main.
|
|
44
|
-
"@dxos/kv-store": "0.5.9-main.
|
|
45
|
-
"@dxos/lock-file": "0.5.9-main.
|
|
46
|
-
"@dxos/log": "0.5.9-main.
|
|
47
|
-
"@dxos/messaging": "0.5.9-main.
|
|
48
|
-
"@dxos/network-manager": "0.5.9-main.
|
|
49
|
-
"@dxos/node-std": "0.5.9-main.
|
|
50
|
-
"@dxos/protocols": "0.5.9-main.
|
|
51
|
-
"@dxos/random-access-storage": "0.5.9-main.
|
|
52
|
-
"@dxos/
|
|
53
|
-
"@dxos/
|
|
54
|
-
"@dxos/teleport-extension-gossip": "0.5.9-main.
|
|
55
|
-
"@dxos/teleport-extension-object-sync": "0.5.9-main.
|
|
56
|
-
"@dxos/timeframe": "0.5.9-main.
|
|
57
|
-
"@dxos/
|
|
58
|
-
"@dxos/
|
|
59
|
-
"@dxos/websocket-rpc": "0.5.9-main.
|
|
26
|
+
"@dxos/async": "0.5.9-main.bf0ae3e",
|
|
27
|
+
"@dxos/client-protocol": "0.5.9-main.bf0ae3e",
|
|
28
|
+
"@dxos/codec-protobuf": "0.5.9-main.bf0ae3e",
|
|
29
|
+
"@dxos/config": "0.5.9-main.bf0ae3e",
|
|
30
|
+
"@dxos/context": "0.5.9-main.bf0ae3e",
|
|
31
|
+
"@dxos/credentials": "0.5.9-main.bf0ae3e",
|
|
32
|
+
"@dxos/automerge": "0.5.9-main.bf0ae3e",
|
|
33
|
+
"@dxos/debug": "0.5.9-main.bf0ae3e",
|
|
34
|
+
"@dxos/crypto": "0.5.9-main.bf0ae3e",
|
|
35
|
+
"@dxos/echo-pipeline": "0.5.9-main.bf0ae3e",
|
|
36
|
+
"@dxos/echo-db": "0.5.9-main.bf0ae3e",
|
|
37
|
+
"@dxos/echo-protocol": "0.5.9-main.bf0ae3e",
|
|
38
|
+
"@dxos/echo-schema": "0.5.9-main.bf0ae3e",
|
|
39
|
+
"@dxos/feed-store": "0.5.9-main.bf0ae3e",
|
|
40
|
+
"@dxos/indexing": "0.5.9-main.bf0ae3e",
|
|
41
|
+
"@dxos/invariant": "0.5.9-main.bf0ae3e",
|
|
42
|
+
"@dxos/keyring": "0.5.9-main.bf0ae3e",
|
|
43
|
+
"@dxos/keys": "0.5.9-main.bf0ae3e",
|
|
44
|
+
"@dxos/kv-store": "0.5.9-main.bf0ae3e",
|
|
45
|
+
"@dxos/lock-file": "0.5.9-main.bf0ae3e",
|
|
46
|
+
"@dxos/log": "0.5.9-main.bf0ae3e",
|
|
47
|
+
"@dxos/messaging": "0.5.9-main.bf0ae3e",
|
|
48
|
+
"@dxos/network-manager": "0.5.9-main.bf0ae3e",
|
|
49
|
+
"@dxos/node-std": "0.5.9-main.bf0ae3e",
|
|
50
|
+
"@dxos/protocols": "0.5.9-main.bf0ae3e",
|
|
51
|
+
"@dxos/random-access-storage": "0.5.9-main.bf0ae3e",
|
|
52
|
+
"@dxos/rpc": "0.5.9-main.bf0ae3e",
|
|
53
|
+
"@dxos/teleport": "0.5.9-main.bf0ae3e",
|
|
54
|
+
"@dxos/teleport-extension-gossip": "0.5.9-main.bf0ae3e",
|
|
55
|
+
"@dxos/teleport-extension-object-sync": "0.5.9-main.bf0ae3e",
|
|
56
|
+
"@dxos/timeframe": "0.5.9-main.bf0ae3e",
|
|
57
|
+
"@dxos/tracing": "0.5.9-main.bf0ae3e",
|
|
58
|
+
"@dxos/util": "0.5.9-main.bf0ae3e",
|
|
59
|
+
"@dxos/websocket-rpc": "0.5.9-main.bf0ae3e"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/platform": "^1.3.4",
|
|
63
63
|
"@types/readable-stream": "^2.3.9",
|
|
64
|
-
"@dxos/signal": "0.5.9-main.
|
|
64
|
+
"@dxos/signal": "0.5.9-main.bf0ae3e"
|
|
65
65
|
},
|
|
66
66
|
"publishConfig": {
|
|
67
67
|
"access": "public"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type CredentialProcessor, getCredentialAssertion } from '@dxos/credentials';
|
|
6
|
+
import { SpaceId, type PublicKey } from '@dxos/keys';
|
|
7
|
+
import { log } from '@dxos/log';
|
|
8
|
+
import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
9
|
+
|
|
10
|
+
type DefaultSpaceStateMachineParams = {
|
|
11
|
+
identityKey: PublicKey;
|
|
12
|
+
onUpdate?: () => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Processes device invitation credentials.
|
|
17
|
+
*/
|
|
18
|
+
export class DefaultSpaceStateMachine implements CredentialProcessor {
|
|
19
|
+
private _spaceId: SpaceId | undefined;
|
|
20
|
+
|
|
21
|
+
constructor(private readonly _params: DefaultSpaceStateMachineParams) {}
|
|
22
|
+
|
|
23
|
+
public get spaceId(): SpaceId | undefined {
|
|
24
|
+
return this._spaceId;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async processCredential(credential: Credential) {
|
|
28
|
+
const assertion = getCredentialAssertion(credential);
|
|
29
|
+
switch (assertion['@type']) {
|
|
30
|
+
case 'dxos.halo.credentials.DefaultSpace': {
|
|
31
|
+
if (!credential.subject.id.equals(this._params.identityKey)) {
|
|
32
|
+
log.warn('Invalid default space credential', { expectedIdentity: this._params.identityKey, credential });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!SpaceId.isValid(assertion.spaceId)) {
|
|
36
|
+
log.warn('Invalid default space id', { id: assertion.spaceId });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this._spaceId = assertion.spaceId;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -24,11 +24,7 @@ describe('IdentityService', () => {
|
|
|
24
24
|
beforeEach(async () => {
|
|
25
25
|
serviceContext = await createServiceContext();
|
|
26
26
|
await serviceContext.open(new Context());
|
|
27
|
-
identityService =
|
|
28
|
-
(options) => serviceContext.createIdentity(options),
|
|
29
|
-
serviceContext.identityManager,
|
|
30
|
-
serviceContext.keyring,
|
|
31
|
-
);
|
|
27
|
+
identityService = createIdentityService(serviceContext);
|
|
32
28
|
});
|
|
33
29
|
|
|
34
30
|
afterEach(async () => {
|
|
@@ -95,3 +91,37 @@ describe('IdentityService', () => {
|
|
|
95
91
|
});
|
|
96
92
|
});
|
|
97
93
|
});
|
|
94
|
+
|
|
95
|
+
describe('open', () => {
|
|
96
|
+
test('identity without default space fixed', async () => {
|
|
97
|
+
const serviceContext = await createServiceContext();
|
|
98
|
+
await serviceContext.open(new Context());
|
|
99
|
+
const identity = await serviceContext.createIdentity();
|
|
100
|
+
const identityService = createIdentityService(serviceContext);
|
|
101
|
+
const getDataSpaces = () => [...(serviceContext.dataSpaceManager?.spaces?.values() ?? [])];
|
|
102
|
+
expect(getDataSpaces().length).to.eq(0);
|
|
103
|
+
expect(identity.defaultSpaceId).to.be.undefined;
|
|
104
|
+
await identityService.open();
|
|
105
|
+
expect(getDataSpaces()[0].id === identity.defaultSpaceId).to.be.true;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('identity without default space credential fixed', async () => {
|
|
109
|
+
const serviceContext = await createServiceContext();
|
|
110
|
+
await serviceContext.open(new Context());
|
|
111
|
+
const identity = await serviceContext.createIdentity();
|
|
112
|
+
const space = await serviceContext.dataSpaceManager!.createDefaultSpace();
|
|
113
|
+
const identityService = createIdentityService(serviceContext);
|
|
114
|
+
expect(identity.defaultSpaceId).to.be.undefined;
|
|
115
|
+
await identityService.open();
|
|
116
|
+
expect(identity.defaultSpaceId === space.id).to.be.true;
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const createIdentityService = (serviceContext: ServiceContext) => {
|
|
121
|
+
return new IdentityServiceImpl(
|
|
122
|
+
serviceContext.identityManager,
|
|
123
|
+
serviceContext.keyring,
|
|
124
|
+
() => serviceContext.dataSpaceManager!,
|
|
125
|
+
(options) => serviceContext.createIdentity(options),
|
|
126
|
+
);
|
|
127
|
+
};
|
|
@@ -3,36 +3,59 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Stream } from '@dxos/codec-protobuf';
|
|
6
|
+
import { Resource } from '@dxos/context';
|
|
6
7
|
import { signPresentation } from '@dxos/credentials';
|
|
7
8
|
import { todo } from '@dxos/debug';
|
|
8
9
|
import { invariant } from '@dxos/invariant';
|
|
9
10
|
import { type Keyring } from '@dxos/keyring';
|
|
10
11
|
import {
|
|
11
12
|
type CreateIdentityRequest,
|
|
12
|
-
type Identity,
|
|
13
|
+
type Identity as IdentityProto,
|
|
13
14
|
type IdentityService,
|
|
14
15
|
type QueryIdentityResponse,
|
|
15
16
|
type RecoverIdentityRequest,
|
|
16
17
|
type SignPresentationRequest,
|
|
18
|
+
SpaceState,
|
|
17
19
|
} from '@dxos/protocols/proto/dxos/client/services';
|
|
18
20
|
import { type Presentation, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
19
21
|
|
|
22
|
+
import { type Identity } from './identity';
|
|
20
23
|
import { type CreateIdentityOptions, type IdentityManager } from './identity-manager';
|
|
24
|
+
import { type DataSpaceManager } from '../spaces';
|
|
21
25
|
|
|
22
|
-
export class IdentityServiceImpl implements IdentityService {
|
|
26
|
+
export class IdentityServiceImpl extends Resource implements IdentityService {
|
|
23
27
|
constructor(
|
|
24
|
-
private readonly _createIdentity: (params: CreateIdentityOptions) => Promise<Identity>,
|
|
25
28
|
private readonly _identityManager: IdentityManager,
|
|
26
29
|
private readonly _keyring: Keyring,
|
|
30
|
+
private readonly _dataSpaceManagerProvider: () => DataSpaceManager,
|
|
31
|
+
private readonly _createIdentity: (params: CreateIdentityOptions) => Promise<Identity>,
|
|
27
32
|
private readonly _onProfileUpdate?: (profile: ProfileDocument | undefined) => Promise<void>,
|
|
28
|
-
) {
|
|
33
|
+
) {
|
|
34
|
+
super();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
protected override async _open() {
|
|
38
|
+
const identity = this._identityManager.identity;
|
|
39
|
+
if (identity && !identity.defaultSpaceId) {
|
|
40
|
+
await this._fixIdentityWithoutDefaultSpace(identity);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
29
43
|
|
|
30
|
-
async createIdentity(request: CreateIdentityRequest): Promise<
|
|
44
|
+
async createIdentity(request: CreateIdentityRequest): Promise<IdentityProto> {
|
|
31
45
|
await this._createIdentity({ displayName: request.profile?.displayName, deviceProfile: request.deviceProfile });
|
|
46
|
+
const dataSpaceManager = this._dataSpaceManagerProvider();
|
|
47
|
+
await this._createDefaultSpace(dataSpaceManager);
|
|
32
48
|
return this._getIdentity()!;
|
|
33
49
|
}
|
|
34
50
|
|
|
35
|
-
async
|
|
51
|
+
private async _createDefaultSpace(dataSpaceManager: DataSpaceManager) {
|
|
52
|
+
const space = await dataSpaceManager!.createDefaultSpace();
|
|
53
|
+
const identity = this._identityManager.identity;
|
|
54
|
+
invariant(identity);
|
|
55
|
+
await identity.updateDefaultSpace(space.id);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async recoverIdentity(request: RecoverIdentityRequest): Promise<IdentityProto> {
|
|
36
59
|
return todo();
|
|
37
60
|
}
|
|
38
61
|
|
|
@@ -45,7 +68,7 @@ export class IdentityServiceImpl implements IdentityService {
|
|
|
45
68
|
});
|
|
46
69
|
}
|
|
47
70
|
|
|
48
|
-
private _getIdentity():
|
|
71
|
+
private _getIdentity(): IdentityProto | undefined {
|
|
49
72
|
if (!this._identityManager.identity) {
|
|
50
73
|
return undefined;
|
|
51
74
|
}
|
|
@@ -57,7 +80,7 @@ export class IdentityServiceImpl implements IdentityService {
|
|
|
57
80
|
};
|
|
58
81
|
}
|
|
59
82
|
|
|
60
|
-
async updateProfile(profile: ProfileDocument): Promise<
|
|
83
|
+
async updateProfile(profile: ProfileDocument): Promise<IdentityProto> {
|
|
61
84
|
invariant(this._identityManager.identity, 'Identity not initialized.');
|
|
62
85
|
await this._identityManager.updateProfile(profile);
|
|
63
86
|
await this._onProfileUpdate?.(this._identityManager.identity.profileDocument);
|
|
@@ -75,4 +98,23 @@ export class IdentityServiceImpl implements IdentityService {
|
|
|
75
98
|
nonce,
|
|
76
99
|
});
|
|
77
100
|
}
|
|
101
|
+
|
|
102
|
+
private async _fixIdentityWithoutDefaultSpace(identity: Identity) {
|
|
103
|
+
let hasDefaultSpace = false;
|
|
104
|
+
const dataSpaceManager = this._dataSpaceManagerProvider();
|
|
105
|
+
for (const space of dataSpaceManager.spaces.values()) {
|
|
106
|
+
if (space.state === SpaceState.CLOSED) {
|
|
107
|
+
await space.open();
|
|
108
|
+
await space.initializeDataPipeline();
|
|
109
|
+
}
|
|
110
|
+
if (await dataSpaceManager.isDefaultSpace(space)) {
|
|
111
|
+
await identity.updateDefaultSpace(space.id);
|
|
112
|
+
hasDefaultSpace = true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!hasDefaultSpace) {
|
|
117
|
+
await this._createDefaultSpace(dataSpaceManager);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
78
120
|
}
|
|
@@ -16,7 +16,7 @@ import { type Signer } from '@dxos/crypto';
|
|
|
16
16
|
import { type Space } from '@dxos/echo-pipeline';
|
|
17
17
|
import { writeMessages } from '@dxos/feed-store';
|
|
18
18
|
import { invariant } from '@dxos/invariant';
|
|
19
|
-
import { PublicKey } from '@dxos/keys';
|
|
19
|
+
import { PublicKey, type SpaceId } from '@dxos/keys';
|
|
20
20
|
import { log } from '@dxos/log';
|
|
21
21
|
import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
22
22
|
import {
|
|
@@ -26,10 +26,12 @@ import {
|
|
|
26
26
|
} from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
27
27
|
import { type DeviceAdmissionRequest } from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
28
28
|
import { type Presence } from '@dxos/teleport-extension-gossip';
|
|
29
|
+
import { Timeframe } from '@dxos/timeframe';
|
|
29
30
|
import { trace } from '@dxos/tracing';
|
|
30
31
|
import { type ComplexMap, ComplexSet } from '@dxos/util';
|
|
31
32
|
|
|
32
33
|
import { TrustedKeySetAuthVerifier } from './authenticator';
|
|
34
|
+
import { DefaultSpaceStateMachine } from './default-space-state-machine';
|
|
33
35
|
|
|
34
36
|
export type IdentityParams = {
|
|
35
37
|
identityKey: PublicKey;
|
|
@@ -49,6 +51,7 @@ export class Identity {
|
|
|
49
51
|
private readonly _presence?: Presence;
|
|
50
52
|
private readonly _deviceStateMachine: DeviceStateMachine;
|
|
51
53
|
private readonly _profileStateMachine: ProfileStateMachine;
|
|
54
|
+
private readonly _defaultSpaceStateMachine: DefaultSpaceStateMachine;
|
|
52
55
|
public readonly authVerifier: TrustedKeySetAuthVerifier;
|
|
53
56
|
|
|
54
57
|
public readonly identityKey: PublicKey;
|
|
@@ -75,6 +78,10 @@ export class Identity {
|
|
|
75
78
|
identityKey: this.identityKey,
|
|
76
79
|
onUpdate: () => this.stateUpdate.emit(),
|
|
77
80
|
});
|
|
81
|
+
this._defaultSpaceStateMachine = new DefaultSpaceStateMachine({
|
|
82
|
+
identityKey: this.identityKey,
|
|
83
|
+
onUpdate: () => this.stateUpdate.emit(),
|
|
84
|
+
});
|
|
78
85
|
|
|
79
86
|
this.authVerifier = new TrustedKeySetAuthVerifier({
|
|
80
87
|
trustedKeysProvider: () => new ComplexSet(PublicKey.hash, this.authorizedDeviceKeys.keys()),
|
|
@@ -88,17 +95,24 @@ export class Identity {
|
|
|
88
95
|
return this._deviceStateMachine.authorizedDeviceKeys;
|
|
89
96
|
}
|
|
90
97
|
|
|
98
|
+
get defaultSpaceId(): SpaceId | undefined {
|
|
99
|
+
return this._defaultSpaceStateMachine.spaceId;
|
|
100
|
+
}
|
|
101
|
+
|
|
91
102
|
@trace.span()
|
|
92
103
|
async open(ctx: Context) {
|
|
104
|
+
await this._presence?.open();
|
|
93
105
|
await this.space.spaceState.addCredentialProcessor(this._deviceStateMachine);
|
|
94
106
|
await this.space.spaceState.addCredentialProcessor(this._profileStateMachine);
|
|
107
|
+
await this.space.spaceState.addCredentialProcessor(this._defaultSpaceStateMachine);
|
|
95
108
|
await this.space.open(ctx);
|
|
96
109
|
}
|
|
97
110
|
|
|
98
111
|
@trace.span()
|
|
99
112
|
async close(ctx: Context) {
|
|
100
|
-
await this._presence?.
|
|
113
|
+
await this._presence?.close();
|
|
101
114
|
await this.authVerifier.close();
|
|
115
|
+
await this.space.spaceState.removeCredentialProcessor(this._defaultSpaceStateMachine);
|
|
102
116
|
await this.space.spaceState.removeCredentialProcessor(this._profileStateMachine);
|
|
103
117
|
await this.space.spaceState.removeCredentialProcessor(this._deviceStateMachine);
|
|
104
118
|
await this.space.close();
|
|
@@ -157,6 +171,15 @@ export class Identity {
|
|
|
157
171
|
return createCredentialSignerWithKey(this._signer, this.deviceKey);
|
|
158
172
|
}
|
|
159
173
|
|
|
174
|
+
async updateDefaultSpace(spaceId: SpaceId) {
|
|
175
|
+
const credential = await this.getDeviceCredentialSigner().createCredential({
|
|
176
|
+
subject: this.identityKey,
|
|
177
|
+
assertion: { '@type': 'dxos.halo.credentials.DefaultSpace', spaceId },
|
|
178
|
+
});
|
|
179
|
+
const receipt = await this.controlPipeline.writer.write({ credential: { credential } });
|
|
180
|
+
await this.controlPipeline.state.waitUntilTimeframe(new Timeframe([[receipt.feedKey, receipt.seq]]));
|
|
181
|
+
}
|
|
182
|
+
|
|
160
183
|
async admitDevice({ deviceKey, controlFeedKey, dataFeedKey }: DeviceAdmissionRequest) {
|
|
161
184
|
log('Admitting device:', {
|
|
162
185
|
identityKey: this.identityKey,
|
|
@@ -120,10 +120,7 @@ export class ServiceContext extends Resource {
|
|
|
120
120
|
this._runtimeParams as IdentityManagerRuntimeParams,
|
|
121
121
|
);
|
|
122
122
|
|
|
123
|
-
this.echoHost = new EchoHost({
|
|
124
|
-
kv: this.level,
|
|
125
|
-
storage: this.storage,
|
|
126
|
-
});
|
|
123
|
+
this.echoHost = new EchoHost({ kv: this.level });
|
|
127
124
|
|
|
128
125
|
this.invitations = new InvitationsHandler(this.networkManager, _runtimeParams?.invitationConnectionDefaultParams);
|
|
129
126
|
this.invitationsManager = new InvitationsManager(
|
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Event, synchronized } from '@dxos/async';
|
|
6
|
-
import { clientServiceBundle,
|
|
6
|
+
import { clientServiceBundle, type ClientServices } from '@dxos/client-protocol';
|
|
7
7
|
import { type Config } from '@dxos/config';
|
|
8
8
|
import { Context } from '@dxos/context';
|
|
9
|
-
import { type ObjectStructure, encodeReference, type SpaceDoc } from '@dxos/echo-protocol';
|
|
10
|
-
import { getTypeReference } from '@dxos/echo-schema';
|
|
11
9
|
import { invariant } from '@dxos/invariant';
|
|
12
10
|
import { PublicKey } from '@dxos/keys';
|
|
13
11
|
import { type LevelDB } from '@dxos/kv-store';
|
|
@@ -18,7 +16,6 @@ import { trace } from '@dxos/protocols';
|
|
|
18
16
|
import { SystemStatus } from '@dxos/protocols/proto/dxos/client/services';
|
|
19
17
|
import { type Storage } from '@dxos/random-access-storage';
|
|
20
18
|
import { TRACE_PROCESSOR, trace as Trace } from '@dxos/tracing';
|
|
21
|
-
import { assignDeep } from '@dxos/util';
|
|
22
19
|
import { WebsocketRpcClient } from '@dxos/websocket-rpc';
|
|
23
20
|
|
|
24
21
|
import { ServiceContext, type ServiceContextRuntimeParams } from './service-context';
|
|
@@ -86,7 +83,7 @@ export class ClientServicesHost {
|
|
|
86
83
|
private _devtoolsProxy?: WebsocketRpcClient<{}, ClientServices>;
|
|
87
84
|
|
|
88
85
|
private _serviceContext!: ServiceContext;
|
|
89
|
-
private readonly _runtimeParams
|
|
86
|
+
private readonly _runtimeParams: ServiceContextRuntimeParams;
|
|
90
87
|
private diagnosticsBroadcastHandler: CollectDiagnosticsBroadcastHandler;
|
|
91
88
|
|
|
92
89
|
@Trace.info()
|
|
@@ -109,7 +106,7 @@ export class ClientServicesHost {
|
|
|
109
106
|
this._storage = storage;
|
|
110
107
|
this._level = level;
|
|
111
108
|
this._callbacks = callbacks;
|
|
112
|
-
this._runtimeParams = runtimeParams;
|
|
109
|
+
this._runtimeParams = runtimeParams ?? {};
|
|
113
110
|
|
|
114
111
|
if (config) {
|
|
115
112
|
this.initialize({ config, transportFactory, signalManager });
|
|
@@ -254,15 +251,17 @@ export class ClientServicesHost {
|
|
|
254
251
|
this._runtimeParams,
|
|
255
252
|
);
|
|
256
253
|
|
|
254
|
+
const identityService = new IdentityServiceImpl(
|
|
255
|
+
this._serviceContext.identityManager,
|
|
256
|
+
this._serviceContext.keyring,
|
|
257
|
+
() => this._serviceContext.dataSpaceManager!,
|
|
258
|
+
(params) => this._createIdentity(params),
|
|
259
|
+
(profile) => this._serviceContext.broadcastProfileUpdate(profile),
|
|
260
|
+
);
|
|
261
|
+
|
|
257
262
|
this._serviceRegistry.setServices({
|
|
258
263
|
SystemService: this._systemService,
|
|
259
|
-
|
|
260
|
-
IdentityService: new IdentityServiceImpl(
|
|
261
|
-
(params) => this._createIdentity(params),
|
|
262
|
-
this._serviceContext.identityManager,
|
|
263
|
-
this._serviceContext.keyring,
|
|
264
|
-
(profile) => this._serviceContext.broadcastProfileUpdate(profile),
|
|
265
|
-
),
|
|
264
|
+
IdentityService: identityService,
|
|
266
265
|
|
|
267
266
|
InvitationsService: new InvitationsServiceImpl(this._serviceContext.invitationsManager),
|
|
268
267
|
|
|
@@ -294,6 +293,7 @@ export class ClientServicesHost {
|
|
|
294
293
|
});
|
|
295
294
|
|
|
296
295
|
await this._serviceContext.open(ctx);
|
|
296
|
+
await identityService.open();
|
|
297
297
|
|
|
298
298
|
const devtoolsProxy = this._config?.get('runtime.client.devtoolsProxy');
|
|
299
299
|
if (devtoolsProxy) {
|
|
@@ -349,34 +349,7 @@ export class ClientServicesHost {
|
|
|
349
349
|
|
|
350
350
|
private async _createIdentity(params: CreateIdentityOptions) {
|
|
351
351
|
const identity = await this._serviceContext.createIdentity(params);
|
|
352
|
-
|
|
353
|
-
// Setup default space.
|
|
354
352
|
await this._serviceContext.initialized.wait();
|
|
355
|
-
const space = await this._serviceContext.dataSpaceManager!.createSpace();
|
|
356
|
-
|
|
357
|
-
const automergeIndex = space.automergeSpaceState.rootUrl;
|
|
358
|
-
invariant(automergeIndex);
|
|
359
|
-
const document = this._serviceContext.echoHost.automergeRepo.find<SpaceDoc>(automergeIndex as any);
|
|
360
|
-
await document.whenReady();
|
|
361
|
-
|
|
362
|
-
// TODO(dmaretskyi): Better API for low-level data access.
|
|
363
|
-
const properties: ObjectStructure = {
|
|
364
|
-
system: {
|
|
365
|
-
type: encodeReference(getTypeReference(PropertiesType)!),
|
|
366
|
-
},
|
|
367
|
-
data: {
|
|
368
|
-
[defaultKey]: identity.identityKey.toHex(),
|
|
369
|
-
},
|
|
370
|
-
meta: {
|
|
371
|
-
keys: [],
|
|
372
|
-
},
|
|
373
|
-
};
|
|
374
|
-
const propertiesId = PublicKey.random().toHex();
|
|
375
|
-
document.change((doc: SpaceDoc) => {
|
|
376
|
-
assignDeep(doc, ['objects', propertiesId], properties);
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
await this._serviceContext.echoHost.flush();
|
|
380
353
|
return identity;
|
|
381
354
|
}
|
|
382
355
|
}
|
|
@@ -12,7 +12,7 @@ import { log } from '@dxos/log';
|
|
|
12
12
|
import { SpaceState } from '@dxos/protocols/proto/dxos/client/services';
|
|
13
13
|
import { describe, openAndClose, test } from '@dxos/test';
|
|
14
14
|
|
|
15
|
-
import { TestBuilder } from '../testing';
|
|
15
|
+
import { TestBuilder, type TestPeer } from '../testing';
|
|
16
16
|
|
|
17
17
|
describe('DataSpaceManager', () => {
|
|
18
18
|
test('create space', async () => {
|
|
@@ -168,5 +168,50 @@ describe('DataSpaceManager', () => {
|
|
|
168
168
|
500,
|
|
169
169
|
);
|
|
170
170
|
});
|
|
171
|
+
|
|
172
|
+
test('activate opens a lazily loaded space', async () => {
|
|
173
|
+
const builder = new TestBuilder();
|
|
174
|
+
|
|
175
|
+
const peer = builder.createPeer();
|
|
176
|
+
await peer.createIdentity();
|
|
177
|
+
await openAndClose(peer.echoHost, peer.dataSpaceManager);
|
|
178
|
+
|
|
179
|
+
await peer.dataSpaceManager.createSpace();
|
|
180
|
+
await reloadDataSpaces(peer);
|
|
181
|
+
|
|
182
|
+
const space = getFirstSpace(peer);
|
|
183
|
+
expect(space.state).to.equal(SpaceState.CLOSED);
|
|
184
|
+
await space.activate();
|
|
185
|
+
await asyncTimeout(
|
|
186
|
+
space.stateUpdate.waitForCondition(() => space.state === SpaceState.READY),
|
|
187
|
+
500,
|
|
188
|
+
);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('deactivate lazily loaded space ', async () => {
|
|
192
|
+
const builder = new TestBuilder();
|
|
193
|
+
|
|
194
|
+
const peer = builder.createPeer();
|
|
195
|
+
await peer.createIdentity();
|
|
196
|
+
await openAndClose(peer.echoHost, peer.dataSpaceManager);
|
|
197
|
+
|
|
198
|
+
await peer.dataSpaceManager.createSpace();
|
|
199
|
+
await reloadDataSpaces(peer);
|
|
200
|
+
|
|
201
|
+
await getFirstSpace(peer).deactivate();
|
|
202
|
+
|
|
203
|
+
await reloadDataSpaces(peer);
|
|
204
|
+
|
|
205
|
+
expect(getFirstSpace(peer).state).to.eq(SpaceState.INACTIVE);
|
|
206
|
+
});
|
|
171
207
|
});
|
|
208
|
+
|
|
209
|
+
const reloadDataSpaces = async (peer: TestPeer) => {
|
|
210
|
+
await peer.dataSpaceManager.close();
|
|
211
|
+
await peer.dataSpaceManager.open();
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const getFirstSpace = (peer: TestPeer) => {
|
|
215
|
+
return Array.from(peer.dataSpaceManager.spaces.values())[0];
|
|
216
|
+
};
|
|
172
217
|
});
|